From 48aa4e28d6812ced004b56c83484de03b19526fa Mon Sep 17 00:00:00 2001 From: Kai-Hwa Yao Date: Tue, 29 Oct 2019 18:07:33 -0700 Subject: [PATCH] Falcor 4.0 Preview (#222) * 4.0 dev snapshot --- .editorconfig | 5 + .gitignore | 24 +- Build/deploycommon.bat | 48 + Build/deployproject.bat | 7 + {packman => Build/packman}/config.packman.xml | 0 {packman => Build/packman}/packman | 0 {packman => Build/packman}/packman.cmd | 0 .../packman}/win-bootstrap/configure.bat | 0 .../win-bootstrap/fetch_file_from_s3.cmd | 0 .../win-bootstrap/fetch_file_from_s3.ps1 | 0 .../win-bootstrap/fetch_file_from_url.ps1 | 0 .../win-bootstrap/generate_temp_file_name.ps1 | 0 .../win-bootstrap/generate_temp_folder.ps1 | 0 .../packman}/win-bootstrap/install_package.py | 0 Build/patchpropssheet.py | 41 + .../BuildScripts => Build}/prebuild.bat | 13 +- Build/update_dependencies.bat | 10 + .../update_dependencies.sh | 0 CHANGELOG.md | 43 +- ...CodeConvention.cpp => CodingConvention.cpp | 10 +- Falcor.sln | 434 +- .../PatchFalcorPropertySheet.exe | Bin 36352 -> 0 bytes .../PatchFalcorPropertySheet.cpp | 104 - .../PatchFalcorPropertySheet.filters | 22 - .../PatchFalcorPropertySheet.sln | 22 - .../PatchFalcorPropertySheet.vcxproj | 87 - Framework/BuildScripts/movedata.bat | 37 - Framework/BuildScripts/moveprojectdata.bat | 27 - Framework/BuildScripts/postbuild.bat | 37 - .../FalcorSharedObjects.vcxproj | 190 - .../FalcorSharedObjects.vcxproj.filters | 63 - Framework/Source/API/Buffer.cpp | 139 - Framework/Source/API/ComputeContext.cpp | 111 - .../Source/API/D3D12/D3D12ResourceViews.cpp | 233 - Framework/Source/API/D3D12/D3DViews.h | 264 - Framework/Source/API/FBO.cpp | 331 -- Framework/Source/API/Texture.cpp | 195 - .../Source/Effects/AmbientOcclusion/SSAO.cpp | 282 - .../Source/Effects/AmbientOcclusion/SSAO.h | 160 - Framework/Source/Effects/FXAA/FXAA.cpp | 94 - .../Source/Effects/NormalMap/LeanMap.cpp | 166 - Framework/Source/Effects/SkyBox/SkyBox.cpp | 215 - Framework/Source/Effects/SkyBox/SkyBox.h | 140 - Framework/Source/Effects/TAA/TAA.cpp | 129 - Framework/Source/Effects/TAA/TAA.h | 115 - .../Effects/ToneMapping/ToneMapping.cpp | 268 - .../Source/Effects/ToneMapping/ToneMapping.h | 194 - .../Source/Effects/Utils/GaussianBlur.cpp | 202 - Framework/Source/Effects/Utils/GaussianBlur.h | 111 - .../Experimental/Raytracing/RtModel.cpp | 263 - .../Experimental/Raytracing/RtScene.cpp | 309 - .../Source/Experimental/Raytracing/RtScene.h | 92 - .../Raytracing/RtSceneRenderer.cpp | 215 - .../Experimental/Raytracing/RtSceneRenderer.h | 81 - .../RenderGraph/RenderGraphImportExport.cpp | 142 - .../RenderGraph/RenderGraphScripting.cpp | 153 - .../RenderGraph/RenderPassReflection.cpp | 154 - .../RenderGraph/ResourceCache.cpp | 313 - .../Experimental/RenderPasses/ImageLoader.cpp | 159 - Framework/Source/Falcor.h | 151 - Framework/Source/Falcor.props | 33 - Framework/Source/Falcor.vcxproj | 1537 ----- Framework/Source/Falcor.vcxproj.filters | 2836 --------- Framework/Source/Graphics/FboHelper.cpp | 161 - Framework/Source/Graphics/FboHelper.h | 64 - Framework/Source/Graphics/FullScreenPass.cpp | 159 - Framework/Source/Graphics/FullScreenPass.h | 117 - Framework/Source/Graphics/Model/Animation.cpp | 149 - Framework/Source/Graphics/Model/Animation.h | 85 - .../Graphics/Model/AnimationController.cpp | 144 - .../Graphics/Model/AnimationController.h | 90 - .../Model/Loaders/AssimpModelImporter.cpp | 994 ---- .../Model/Loaders/AssimpModelImporter.h | 110 - .../Graphics/Model/Loaders/BinaryImage.cpp | 1141 ---- .../Graphics/Model/Loaders/BinaryImage.hpp | 251 - .../Model/Loaders/BinaryModelExporter.cpp | 455 -- .../Model/Loaders/BinaryModelExporter.h | 76 - .../Model/Loaders/BinaryModelImporter.cpp | 980 ---- .../Graphics/Model/Loaders/BinaryModelSpec.h | 156 - .../Model/Loaders/SimpleModelImporter.cpp | 193 - .../Model/Loaders/SimpleModelImporter.h | 92 - .../Model/Loaders/TeroBinaryCode/Image.cpp | 1140 ---- .../Model/Loaders/TeroBinaryCode/Image.hpp | 248 - Framework/Source/Graphics/Model/Mesh.cpp | 98 - Framework/Source/Graphics/Model/Mesh.h | 156 - Framework/Source/Graphics/Model/Model.cpp | 476 -- Framework/Source/Graphics/Model/Model.h | 313 - .../Source/Graphics/Model/ObjectInstance.h | 336 -- .../Source/Graphics/Model/SkinningCache.cpp | 306 - .../Source/Graphics/Model/SkinningCache.h | 125 - .../Source/Graphics/Paths/ObjectPath.cpp | 266 - Framework/Source/Graphics/Paths/ObjectPath.h | 201 - .../Source/Graphics/Paths/PathEditor.cpp | 239 - Framework/Source/Graphics/Paths/PathEditor.h | 96 - .../Graphics/Scene/Editor/SceneEditor.cpp | 1584 ------ .../Graphics/Scene/Editor/SceneEditor.h | 307 - .../Scene/Editor/SceneEditorRenderer.cpp | 121 - .../Scene/Editor/SceneEditorRenderer.h | 68 - Framework/Source/Graphics/Scene/Scene.cpp | 441 -- Framework/Source/Graphics/Scene/Scene.h | 298 - .../Graphics/Scene/SceneExportImportCommon.h | 105 - .../Source/Graphics/Scene/SceneExporter.cpp | 469 -- .../Source/Graphics/Scene/SceneExporter.h | 79 - .../Source/Graphics/Scene/SceneImporter.h | 107 - .../Source/Graphics/Scene/SceneRenderer.cpp | 428 -- .../Source/Graphics/Scene/SceneRenderer.h | 165 - Framework/Source/Sample.cpp | 703 --- Framework/Source/Sample.h | 203 - .../Source/ShadingUtils/Raytracing.slang | 179 - .../Source/ShadingUtils/ShadingUtilsTests.cpp | 100 - Framework/Source/Utils/Gui.cpp | 1043 ---- Framework/Source/Utils/Gui.h | 462 -- Framework/Source/Utils/Logger.cpp | 149 - Framework/Source/Utils/PythonEmbedding.cpp | 623 -- Framework/Source/Utils/PythonEmbedding.h | 223 - .../Source/Utils/Scripting/ScriptBindings.cpp | 156 - Framework/Source/Utils/TextRenderer.cpp | 205 - Framework/Source/Utils/TextRenderer.h | 116 - Framework/Source/Utils/Video/VideoDecoder.cpp | 338 -- Framework/Source/Utils/Video/VideoDecoder.h | 133 - .../Source/Utils/Video/VideoEncoderUI.cpp | 136 - Framework/Source/VR/OpenVR/VRController.cpp | 264 - Framework/Source/VR/OpenVR/VRController.h | 209 - Framework/Source/VR/OpenVR/VRDisplay.cpp | 218 - Framework/Source/VR/OpenVR/VRDisplay.h | 175 - Framework/Source/VR/OpenVR/VRPlayArea.h | 104 - Framework/Source/VR/OpenVR/VRSystem.cpp | 531 -- Framework/Source/VR/OpenVR/VRSystem.h | 257 - Framework/Source/VR/OpenVR/VRTrackerBox.cpp | 162 - Framework/Source/VR/OpenVR/VRTrackerBox.h | 137 - Framework/Source/VR/VrFbo.cpp | 80 - Makefile | 6 +- Mogwai.sln | 70 + README.md | 63 +- Samples/Core/ComputeShader/ComputeShader.cpp | 133 - .../ComputeShader.vcxproj.filters | 19 - .../Data/RenderForLearning.slang | 118 - .../Data/demo_infer.py | 32 - .../Data/demo_init.py | 85 - .../Data/demo_train.py | 29 - .../LearningWithEmbeddedPython.vcxproj | 123 - ...LearningWithEmbeddedPython.vcxproj.filters | 42 - ...ngWithEmbeddedPython_overloadLoadScene.cpp | 101 - .../LearningWithEmbeddedPython/README.txt | 34 - .../Renderers/LiveTrainingDemo.cpp | 563 -- .../Renderers/LiveTrainingDemo.h | 176 - .../MultiPassPostProcess.cpp | 143 - .../MultiPassPostProcess.vcxproj | 99 - .../MultiPassPostProcess.vcxproj.filters | 22 - Samples/Core/ShaderBuffers/Data/teapot.obj | 5049 ----------------- Samples/Core/ShaderBuffers/ShaderBuffers.cpp | 225 - Samples/Core/ShaderBuffers/ShaderBuffers.h | 78 - .../ShaderBuffers.vcxproj.filters | 22 - .../SimpleDeferred/Data/LightingPass.ps.hlsl | 104 - .../Core/SimpleDeferred/SimpleDeferred.cpp | 369 -- Samples/Core/SimpleDeferred/SimpleDeferred.h | 110 - .../SimpleDeferred.vcxproj.filters | 22 - .../Data/StereoRendering.ps.hlsl | 44 - .../Core/StereoRendering/StereoRendering.cpp | 303 - .../Core/StereoRendering/StereoRendering.h | 80 - .../StereoRendering.vcxproj.filters | 25 - .../AmbientOcclusion/AmbientOcclusion.cpp | 179 - .../AmbientOcclusion/AmbientOcclusion.h | 70 - .../AmbientOcclusion/AmbientOcclusion.vcxproj | 103 - .../AmbientOcclusion.vcxproj.filters | 22 - .../Effects/HDRToneMapping/HDRToneMapping.cpp | 182 - .../HDRToneMapping/HDRToneMapping.filters | 7 - .../Effects/HDRToneMapping/HDRToneMapping.h | 77 - .../HDRToneMapping.vcxproj.filters | 19 - Samples/Effects/HashedAlpha/HashedAlpha.cpp | 175 - .../Effects/HashedAlpha/HashedAlpha.vcxproj | 99 - .../HashedAlpha/HashedAlpha.vcxproj.filters | 19 - Samples/Effects/Particles/Particles.cpp | 256 - Samples/Effects/Particles/Particles.h | 87 - Samples/Effects/Particles/Particles.vcxproj | 93 - .../Particles/Particles.vcxproj.filters | 9 - Samples/Effects/Shadows/Shadows.cpp | 284 - Samples/Effects/Shadows/Shadows.h | 105 - Samples/Effects/Shadows/Shadows.vcxproj | 101 - .../Effects/Shadows/Shadows.vcxproj.filters | 22 - .../Effects/SkyBoxRenderer/SkyBoxRenderer.cpp | 112 - .../Effects/SkyBoxRenderer/SkyBoxRenderer.h | 55 - Samples/ForwardRenderer/Data/ApplyAO.ps.slang | 38 - .../Data/ForwardRenderer.slang | 122 - Samples/ForwardRenderer/ForwardRenderer.cpp | 577 -- Samples/ForwardRenderer/ForwardRenderer.h | 218 - .../ForwardRenderer.vcxproj.filters | 28 - .../ForwardRendererControls.cpp | 376 -- .../ForwardRendererSceneRenderer.cpp | 114 - .../ForwardRendererSceneRenderer.h | 61 - .../PathTracer/Data/GGXGICommon.slang | 224 - .../PathTracer/Data/GGXGIRayGen.slang | 93 - Samples/Raytracing/PathTracer/PathTracer.cpp | 185 - .../PathTracer/PathTracer.vcxproj.filters | 76 - .../PathTracer/RenderPasses/GBuffer.h | 61 - .../PathTracer/RenderPasses/GBufferRaster.cpp | 143 - .../RenderPasses/GGXGlobalIllumination.cpp | 196 - .../RenderPasses/GGXGlobalIllumination.h | 86 - .../RenderPasses/TemporalAccumulation.cpp | 160 - .../RenderPasses/TemporalAccumulation.h | 106 - .../RenderGraphEditor.vcxproj | 135 - .../RenderGraphViewer/RenderGraphViewer.cpp | 581 -- .../RenderGraphViewer/RenderGraphViewer.h | 107 - .../RenderGraphViewer.vcxproj.filters | 19 - .../Testing/testAnimation.py | 20 - .../Testing/testForwardLighting.py | 18 - .../Testing/testToneMapping.py | 14 - .../Data/forward_renderer_with_dll.py | 32 - .../SamplePassLibrary/SamplePassLibrary.cpp | 119 - .../FalcorTest/FalcorTest.vcxproj.Filters | 25 - .../FalcorTest/Tests/ShadingUtilsTests.cpp | 100 - .../LightProbeViewer/Data/UnitSphere.fbx | Bin 32736 -> 0 bytes .../LightProbeViewer/LightProbeViewer.cpp | 344 -- .../Utils/LightProbeViewer/LightProbeViewer.h | 87 - .../LightProbeViewer/LightProbeViewer.vcxproj | 100 - .../LightProbeViewer.vcxproj.filters | 19 - Samples/Utils/ModelViewer/ModelViewer.cpp | 403 -- .../SceneEditor/Data/SceneEditorApp.slang | 44 - Samples/Utils/SceneEditor/SceneEditor.vcxproj | 96 - .../SceneEditor/SceneEditor.vcxproj.filters | 19 - Samples/Utils/SceneEditor/SceneEditorApp.cpp | 178 - Samples/Utils/SceneEditor/SceneEditorApp.h | 58 - .../imguinodegrapheditor.cpp | 6 +- .../imguinodegrapheditor.h | 0 Source/Externals/mikktspace/README.md | 5 + Source/Externals/mikktspace/mikktspace.c | 1890 ++++++ Source/Externals/mikktspace/mikktspace.h | 145 + Source/Externals/xoshiro/README.txt | 9 + Source/Externals/xoshiro/splitmix64.c | 28 + Source/Externals/xoshiro/xoshiro128starstar.c | 72 + .../Falcor/Core}/API/BlendState.cpp | 14 +- .../Falcor/Core}/API/BlendState.h | 7 +- Source/Falcor/Core/API/Buffer.cpp | 210 + .../Falcor/Core}/API/Buffer.h | 90 +- Source/Falcor/Core/API/ComputeContext.cpp | 67 + .../Falcor/Core}/API/ComputeContext.h | 65 +- .../Falcor/Core}/API/ComputeStateObject.cpp | 6 +- .../Falcor/Core}/API/ComputeStateObject.h | 18 +- .../Falcor/Core}/API/CopyContext.cpp | 53 +- .../Falcor/Core}/API/CopyContext.h | 22 +- .../Falcor/Core}/API/D3D12/D3D12ApiData.h | 5 +- .../Falcor/Core}/API/D3D12/D3D12Buffer.cpp | 50 +- .../Core}/API/D3D12/D3D12ComputeContext.cpp | 106 +- .../API/D3D12/D3D12ComputeStateObject.cpp | 14 +- .../Core}/API/D3D12/D3D12CopyContext.cpp | 35 +- .../Core/API/D3D12}/D3D12DescriptorData.h | 2 +- .../Core/API/D3D12}/D3D12DescriptorHeap.cpp | 38 +- .../Core/API/D3D12}/D3D12DescriptorHeap.h | 20 +- .../Core/API/D3D12}/D3D12DescriptorPool.cpp | 21 +- .../Core/API/D3D12}/D3D12DescriptorSet.cpp | 14 +- .../Falcor/Core}/API/D3D12/D3D12Device.cpp | 75 +- .../Falcor/Core}/API/D3D12/D3D12Fbo.cpp | 34 +- .../Falcor/Core/API/D3D12/D3D12Formats.cpp | 6 +- .../Falcor/Core/API/D3D12}/D3D12GpuFence.cpp | 17 +- .../Core/API/D3D12/D3D12GpuMemoryHeap.cpp | 50 +- .../Falcor/Core}/API/D3D12/D3D12GpuTimer.cpp | 8 +- .../API/D3D12/D3D12GraphicsStateObject.cpp | 18 +- .../API/D3D12}/D3D12LowLevelContextData.cpp | 10 +- .../Falcor/Core}/API/D3D12/D3D12NvApiExDesc.h | 3 +- .../Falcor/Core}/API/D3D12/D3D12QueryHeap.cpp | 10 +- .../Core}/API/D3D12/D3D12RasterizerState.cpp | 6 +- .../Core}/API/D3D12/D3D12RenderContext.cpp | 305 +- .../Falcor/Core}/API/D3D12/D3D12Resource.cpp | 16 +- .../Falcor/Core}/API/D3D12/D3D12Resource.h | 4 +- .../Core/API/D3D12/D3D12ResourceViews.cpp | 498 ++ .../Core/API/D3D12}/D3D12RootSignature.cpp | 10 +- .../Falcor/Core}/API/D3D12/D3D12Sampler.cpp | 11 +- .../Falcor/Core/API/D3D12/D3D12Shader.cpp | 8 +- .../Falcor/Core}/API/D3D12/D3D12State.cpp | 17 +- .../Falcor/Core}/API/D3D12/D3D12State.h | 12 +- .../Falcor/Core}/API/D3D12/D3D12Texture.cpp | 22 +- .../Falcor/Core}/API/D3D12/D3D12Vao.cpp | 7 +- .../Falcor/Core}/API/D3D12/FalcorD3D12.h | 12 +- .../Falcor/Core}/API/DepthStencilState.cpp | 11 +- .../Falcor/Core}/API/DepthStencilState.h | 21 +- .../Falcor/Core/API}/DescriptorPool.cpp | 8 +- .../Falcor/Core/API}/DescriptorPool.h | 16 +- .../Falcor/Core}/API/DescriptorSet.cpp | 7 +- .../Falcor/Core}/API/DescriptorSet.h | 18 +- .../Falcor/Core}/API/Device.cpp | 103 +- .../Falcor/Core}/API/Device.h | 65 +- Source/Falcor/Core/API/FBO.cpp | 473 ++ .../Source => Source/Falcor/Core}/API/FBO.h | 75 +- .../Falcor/Core/API}/FencedPool.h | 6 +- .../Falcor/Core}/API/Formats.cpp | 172 +- .../Falcor/Core}/API/Formats.h | 106 +- .../Falcor/Core/API}/GpuFence.h | 8 +- .../Falcor/Core/API/GpuMemoryHeap.cpp | 30 +- .../Falcor/Core/API/GpuMemoryHeap.h | 41 +- .../Falcor/Core}/API/GpuTimer.cpp | 14 +- .../Falcor/Core}/API/GpuTimer.h | 14 +- .../Falcor/Core}/API/GraphicsStateObject.cpp | 13 +- .../Falcor/Core}/API/GraphicsStateObject.h | 37 +- .../Falcor/Core/API}/LowLevelContextData.h | 8 +- .../Falcor/Core}/API/QueryHeap.h | 4 +- .../Falcor/Core/API/RasterizerState.cpp | 21 +- .../Falcor/Core}/API/RasterizerState.h | 24 +- .../Falcor/Core}/API/RenderContext.cpp | 99 +- .../Falcor/Core}/API/RenderContext.h | 124 +- .../Falcor/Core}/API/Resource.cpp | 140 +- .../Falcor/Core}/API/Resource.h | 76 +- Source/Falcor/Core/API/ResourceViews.cpp | 64 + .../Falcor/Core}/API/ResourceViews.h | 79 +- .../Falcor/Core/API}/RootSignature.cpp | 14 +- .../Falcor/Core/API}/RootSignature.h | 11 +- .../Falcor/Core}/API/Sampler.cpp | 30 +- .../Falcor/Core}/API/Sampler.h | 23 +- .../Falcor/Core}/API/Shader.h | 22 +- Source/Falcor/Core/API/Texture.cpp | 352 ++ .../Falcor/Core}/API/Texture.h | 60 +- .../Falcor/Core/API/TextureLoader.cpp | 14 +- .../Source => Source/Falcor/Core}/API/VAO.cpp | 17 +- .../Source => Source/Falcor/Core}/API/VAO.h | 12 +- .../Falcor/Core/API/VertexLayout.cpp | 12 +- .../Falcor/Core}/API/VertexLayout.h | 36 +- .../Falcor/Core}/API/Vulkan/FalcorVK.h | 4 +- .../Falcor/Core}/API/Vulkan/VKBuffer.cpp | 14 +- .../Core}/API/Vulkan/VKComputeContext.cpp | 13 +- .../Core}/API/Vulkan/VKComputeStateObject.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKCopyContext.cpp | 22 +- .../Core/API/Vulkan}/VKDescriptorData.h | 2 +- .../Core/API/Vulkan}/VKDescriptorPool.cpp | 10 +- .../Core/API/Vulkan}/VKDescriptorSet.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKDevice.cpp | 20 +- .../Falcor/Core}/API/Vulkan/VKFbo.cpp | 6 +- .../Falcor/Core}/API/Vulkan/VKFormats.cpp | 4 +- .../Falcor/Core/API/Vulkan}/VKGpuFence.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKGpuTimer.cpp | 4 +- .../API/Vulkan/VKGraphicsStateObject.cpp | 9 +- .../API/Vulkan}/VKLowLevelContextData.cpp | 4 +- .../Core}/API/Vulkan/VKRasterizerState.cpp | 4 +- .../Core}/API/Vulkan/VKRenderContext.cpp | 25 +- .../Core}/API/Vulkan/VKResourceViews.cpp | 4 +- .../Core/API/Vulkan}/VKRootSignature.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKSampler.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKShader.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKSmartHandle.h | 2 +- .../Falcor/Core}/API/Vulkan/VKState.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VKState.h | 2 +- .../Falcor/Core}/API/Vulkan/VKTexture.cpp | 6 +- .../Falcor/Core}/API/Vulkan/VKVao.cpp | 4 +- .../Core/API/Vulkan/VkGpuMemoryHeap.cpp | 12 +- .../Falcor/Core}/API/Vulkan/VkQueryHeap.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VkResource.cpp | 4 +- .../Falcor/Core}/API/Vulkan/VkSmartHandle.cpp | 4 +- .../Core/BufferTypes}/ConstantBuffer.cpp | 31 +- .../Falcor/Core/BufferTypes}/ConstantBuffer.h | 38 +- .../Core/BufferTypes}/StructuredBuffer.cpp | 88 +- .../Core/BufferTypes}/StructuredBuffer.h | 35 +- .../Falcor/Core/BufferTypes}/TypedBuffer.cpp | 34 +- .../Falcor/Core/BufferTypes}/TypedBuffer.h | 17 +- .../Core/BufferTypes}/VariablesBuffer.cpp | 76 +- .../Core/BufferTypes}/VariablesBuffer.h | 28 +- .../Core/BufferTypes}/VariablesBufferUI.cpp | 130 +- .../Core/BufferTypes}/VariablesBufferUI.h | 17 +- .../Falcor/Core}/FalcorConfig.h | 10 +- .../Falcor/Core/Framework.cpp | 27 +- .../Source => Source/Falcor/Core}/Framework.h | 95 +- .../Falcor/Core}/Platform/Linux/Linux.cpp | 59 +- .../Core}/Platform/Linux/ProgressBarLinux.cpp | 11 +- .../Falcor/Core/Platform}/MonitorInfo.cpp | 9 +- .../Falcor/Core/Platform}/MonitorInfo.h | 11 +- .../Falcor/Core}/Platform/OS.cpp | 21 +- .../Falcor/Core}/Platform/OS.h | 136 +- Source/Falcor/Core/Platform/ProgressBar.cpp | 58 + .../Falcor/Core}/Platform/ProgressBar.h | 24 +- .../Core}/Platform/Windows/ProgressBarWin.cpp | 26 +- .../Falcor/Core}/Platform/Windows/Windows.cpp | 141 +- .../Falcor/Core}/Program/ComputeProgram.cpp | 20 +- .../Falcor/Core}/Program/ComputeProgram.h | 13 +- .../Falcor/Core}/Program/GraphicsProgram.cpp | 11 +- .../Falcor/Core}/Program/GraphicsProgram.h | 16 +- .../Falcor/Core}/Program/ParameterBlock.cpp | 154 +- .../Falcor/Core}/Program/ParameterBlock.h | 57 +- .../Falcor/Core}/Program/Program.cpp | 52 +- .../Falcor/Core}/Program/Program.h | 38 +- .../Core}/Program/ProgramReflection.cpp | 146 +- .../Falcor/Core}/Program/ProgramReflection.h | 35 +- .../Falcor/Core}/Program/ProgramVars.cpp | 53 +- .../Falcor/Core}/Program/ProgramVars.h | 67 +- .../Falcor/Core/Program/ProgramVarsHelpers.h | 159 + .../Falcor/Core}/Program/ProgramVersion.cpp | 24 +- .../Falcor/Core}/Program/ProgramVersion.h | 20 +- .../Falcor/Core}/Program/ShaderLibrary.cpp | 6 +- .../Falcor/Core}/Program/ShaderLibrary.h | 7 +- .../Source => Source/Falcor/Core}/Renderer.h | 154 +- Source/Falcor/Core/Sample.cpp | 640 +++ Source/Falcor/Core/Sample.h | 148 + .../Falcor/Core/State}/ComputeState.cpp | 15 +- .../Falcor/Core/State}/ComputeState.h | 15 +- .../Falcor/Core/State}/GraphicsState.cpp | 16 +- .../Falcor/Core/State}/GraphicsState.h | 22 +- .../Falcor/Core/State/StateGraph.h | 14 +- .../API => Source/Falcor/Core}/Window.cpp | 54 +- .../API => Source/Falcor/Core}/Window.h | 31 +- .../Falcor}/Data/ApplyAO.ps.slang | 2 +- .../Data/Effects/CascadedShadowMap.slang | 2 +- .../Falcor}/Data/Effects/CsmData.h | 6 +- .../Falcor}/Data/Effects/DepthPass.slang | 15 +- .../Falcor}/Data/Effects/FXAA.slang | 2 +- .../Data/Effects/GaussianBlur.ps.slang | 6 +- .../Falcor}/Data/Effects/LeanMapping.slang | 2 +- .../Data/Effects/ParticleConstColor.ps.slang | 2 +- .../Falcor}/Data/Effects/ParticleData.h | 4 +- .../Data/Effects/ParticleEmit.cs.slang | 2 +- .../Data/Effects/ParticleInterpColor.ps.slang | 2 +- .../Data/Effects/ParticleSimulate.cs.slang | 2 +- .../Data/Effects/ParticleSort.cs.slang | 2 +- .../Data/Effects/ParticleTexture.ps.slang | 2 +- .../Data/Effects/ParticleVertex.vs.slang | 2 +- .../Falcor}/Data/Effects/SSAO.ps.slang | 13 +- .../Falcor}/Data/Effects/SSAOData.h | 7 +- .../Falcor}/Data/Effects/ShadowPass.slang | 15 +- .../Falcor}/Data/Effects/SkyBox.slang | 9 +- .../Falcor}/Data/Effects/TAA.ps.slang | 2 +- .../Falcor}/Data/Effects/ToneMapping.ps.slang | 34 +- Source/Falcor/Data/Effects/ToneMappingData.h | 47 + .../Data/Effects/VisibilityPass.ps.slang | 2 +- .../Falcor}/Data/Effects/cube.obj | 0 .../Fonts/DejaVu Sans Mono14.000000.bin | Bin .../Fonts/DejaVu Sans Mono14.000000.dds | Bin .../Falcor}/Data/Framework/Fonts/consolab.ttf | Bin .../Falcor}/Data/Framework/Fonts/trebucbd.ttf | Bin .../Falcor}/Data/Framework/Models/Camera.obj | 0 .../Data/Framework/Models/LightBulb.obj | 0 .../Data/Framework/Models/RotateGizmo.obj | 0 .../Data/Framework/Models/ScaleGizmo.obj | 0 .../Data/Framework/Models/TranslateGizmo.obj | 0 .../Falcor}/Data/Framework/Nvidia.ico | Bin .../Falcor/Data/Framework/Shaders/Blit.slang | 27 +- .../Shaders/ComputeSkinning.cs.slang | 34 +- .../Framework/Shaders/FullScreenPass.gs.slang | 2 +- .../Framework/Shaders/FullScreenPass.vs.slang | 2 +- .../Falcor}/Data/Framework/Shaders/Gui.slang | 2 +- .../Shaders/LightProbeIntegration.ps.slang | 59 +- .../Framework/Shaders/MaterialBlock.slang | 4 +- .../Shaders/ParallelReduction.ps.slang | 2 +- .../Data/Framework/Shaders/SceneBlock.slang | 13 +- .../Data/Framework/Shaders/SceneEditor.slang | 8 +- .../Data/Framework/Shaders/TextRenderer.slang | 2 +- .../Data/Framework/Textures/NextFrame.jpg | Bin 0 -> 19067 bytes .../Falcor/Data/Framework/Textures/Pause.jpg | Bin 0 -> 15778 bytes .../Falcor/Data/Framework/Textures/Play.jpg | Bin 0 -> 16342 bytes .../Data/Framework/Textures/PrevFrame.jpg | Bin 0 -> 19017 bytes .../Falcor/Data/Framework/Textures/Rewind.jpg | Bin 0 -> 18715 bytes .../Falcor/Data/Framework/Textures/Stop.jpg | Bin 0 -> 16884 bytes .../Falcor}/Data/HostDeviceData.h | 2 +- .../Falcor}/Data/HostDeviceData.slang | 2 +- .../Falcor}/Data/HostDeviceSharedCode.h | 152 +- .../Falcor}/Data/HostDeviceSharedMacros.h | 11 +- .../Falcor/Data/Raster.slang | 144 +- .../Data/RenderPasses}/DepthPass.ps.slang | 15 +- .../RenderPasses/ForwardLightingPass.slang | 24 +- .../Falcor}/Data/ShaderCommon.slang | 90 +- .../Falcor}/Data/UnitTest.cs.hlsl | 0 .../Falcor}/Data/VertexAttrib.h | 29 +- .../Effects/AmbientOcclusion/SSAOPass.cpp | 283 + .../Effects/AmbientOcclusion/SSAOPass.h | 101 + Source/Falcor/Effects/FXAA/FXAAPass.cpp | 129 + .../Falcor/Effects/FXAA/FXAAPass.h | 67 +- .../Falcor}/Effects/Shadows/CSM.cpp | 895 +-- .../Falcor}/Effects/Shadows/CSM.h | 210 +- Source/Falcor/Effects/SkyBox/SkyBox.cpp | 211 + Source/Falcor/Effects/SkyBox/SkyBox.h | 76 + Source/Falcor/Effects/TAA/TAAPass.cpp | 135 + .../Falcor/Effects/TAA/TAAPass.h | 53 +- .../Effects/ToneMapping/ToneMappingPass.cpp | 350 ++ .../Effects/ToneMapping/ToneMappingPass.h | 134 + .../Falcor/Effects/Utils/GaussianBlurPass.cpp | 230 + .../Falcor/Effects/Utils/GaussianBlurPass.h | 72 + .../Scene/Lights/EmissiveIntegrator.ps.slang | 146 + .../Scene/Lights/EmissiveLightSampler.cpp | 22 +- .../Scene/Lights/EmissiveLightSampler.h | 111 + .../Scene/Lights/EmissiveLightSampler.slang | 60 +- .../Lights/EmissiveLightSamplerHelpers.slang | 119 + .../EmissiveLightSamplerInterface.slang | 77 + .../Scene/Lights/EmissiveLightSamplerType.h | 73 +- .../Scene/Lights/EmissiveUniformSampler.cpp | 128 + .../Scene/Lights/EmissiveUniformSampler.h | 119 + .../Scene/Lights/EmissiveUniformSampler.slang | 97 + .../Experimental/Scene/Lights/EnvProbe.cpp | 169 + .../Experimental/Scene/Lights/EnvProbe.h | 85 + .../Experimental/Scene/Lights/EnvProbe.slang | 157 + .../Scene/Lights/EnvProbeSetup.cs.slang | 75 + .../Scene/Lights/LightCollection.cpp | 652 +++ .../Scene/Lights/LightCollection.cs.slang | 164 + .../Scene/Lights/LightCollection.h | 265 + .../Scene/Lights/LightCollection.slang | 169 + .../Scene/Lights/LightCollectionShared.h | 45 + .../Scene/Lights/LightHelpers.slang | 247 + .../Experimental/Scene/Lights/MeshLightData.h | 75 + .../Scene/Material/MaterialHelpers.slang | 207 + .../Scene/Material/MaterialShading.slang | 1053 ++++ Source/Falcor/Falcor.h | 183 + .../Source => Source/Falcor}/Falcor.natvis | 0 Source/Falcor/Falcor.props | 47 + Source/Falcor/Falcor.vcxproj | 1155 ++++ Source/Falcor/Falcor.vcxproj.filters | 1575 +++++ Source/Falcor/FalcorExperimental.h | 31 + .../Raytracing/RtProgram/HitProgram.cpp | 5 +- .../Falcor}/Raytracing/RtProgram/HitProgram.h | 4 +- .../Raytracing/RtProgram/RtProgram.cpp | 13 +- .../Falcor}/Raytracing/RtProgram/RtProgram.h | 25 +- .../Raytracing/RtProgram/RtProgramVersion.cpp | 11 +- .../Raytracing/RtProgram/RtProgramVersion.h | 12 +- .../RtProgram/SingleShaderProgram.h | 6 +- .../Falcor}/Raytracing/RtProgramVars.cpp | 88 +- .../Falcor}/Raytracing/RtProgramVars.h | 22 +- .../Raytracing/RtProgramVarsHelper.cpp | 24 +- .../Falcor}/Raytracing/RtProgramVarsHelper.h | 16 +- .../Falcor}/Raytracing/RtShader.cpp | 3 +- .../Falcor}/Raytracing/RtShader.h | 6 +- .../Falcor}/Raytracing/RtState.cpp | 4 +- .../Falcor}/Raytracing/RtState.h | 8 +- .../Falcor}/Raytracing/RtStateObject.cpp | 5 +- .../Falcor}/Raytracing/RtStateObject.h | 10 +- .../Falcor}/Raytracing/RtStateObjectHelper.h | 2 +- .../BasePasses/BaseGraphicsPass.cpp | 62 + .../RenderGraph/BasePasses/BaseGraphicsPass.h | 74 + .../RenderGraph/BasePasses/ComputePass.cpp | 80 + .../RenderGraph/BasePasses/ComputePass.h | 97 + .../RenderGraph/BasePasses/FullScreenPass.cpp | 161 + .../RenderGraph/BasePasses/FullScreenPass.h | 66 + .../RenderGraph/BasePasses/RasterPass.cpp | 61 +- .../RenderGraph/BasePasses/RasterPass.h | 75 + .../BasePasses/RasterScenePass.cpp | 73 + .../RenderGraph/BasePasses/RasterScenePass.h | 77 + .../Falcor}/RenderGraph/RenderGraph.cpp | 622 +- .../Falcor}/RenderGraph/RenderGraph.h | 96 +- .../RenderGraph/RenderGraphCompiler.cpp | 437 ++ .../Falcor/RenderGraph/RenderGraphCompiler.h | 73 +- Source/Falcor/RenderGraph/RenderGraphExe.cpp | 107 + Source/Falcor/RenderGraph/RenderGraphExe.h | 97 + .../Falcor}/RenderGraph/RenderGraphIR.cpp | 66 +- .../Falcor}/RenderGraph/RenderGraphIR.h | 23 +- .../RenderGraph/RenderGraphImportExport.cpp | 176 + .../RenderGraph/RenderGraphImportExport.h | 30 +- .../Falcor}/RenderGraph/RenderGraphUI.cpp | 358 +- .../Falcor}/RenderGraph/RenderGraphUI.h | 32 +- .../Falcor}/RenderGraph/RenderPass.cpp | 11 +- .../Falcor}/RenderGraph/RenderPass.h | 96 +- .../Falcor/RenderGraph/RenderPassHelpers.h | 23 +- .../Falcor}/RenderGraph/RenderPassLibrary.cpp | 65 +- .../Falcor}/RenderGraph/RenderPassLibrary.h | 18 +- .../RenderGraph/RenderPassReflection.cpp | 277 + .../RenderGraph/RenderPassReflection.h | 72 +- .../RenderGraph/RenderPassStandardFlags.h | 38 +- Source/Falcor/RenderGraph/ResourceCache.cpp | 192 + .../Falcor}/RenderGraph/ResourceCache.h | 49 +- .../Falcor}/RenderPasses/BlitPass.cpp | 41 +- .../Falcor}/RenderPasses/BlitPass.h | 22 +- .../Falcor}/RenderPasses/DepthPass.cpp | 50 +- .../Falcor}/RenderPasses/DepthPass.h | 28 +- .../RenderPasses/ForwardLightingPass.cpp | 88 +- .../RenderPasses/ForwardLightingPass.h | 33 +- Source/Falcor/RenderPasses/ImageLoader.cpp | 129 + .../Falcor}/RenderPasses/ImageLoader.h | 25 +- .../Falcor}/RenderPasses/ResolvePass.cpp | 18 +- .../Falcor}/RenderPasses/ResolvePass.h | 16 +- Source/Falcor/Scene/Animation/Animation.cpp | 162 + Source/Falcor/Scene/Animation/Animation.h | 108 + .../Scene/Animation/AnimationController.cpp | 263 + .../Scene/Animation/AnimationController.h | 152 + .../Falcor/Scene}/Camera/Camera.cpp | 97 +- .../Falcor/Scene}/Camera/Camera.h | 78 +- .../Falcor/Scene}/Camera/CameraController.cpp | 123 +- .../Falcor/Scene}/Camera/CameraController.h | 67 +- .../Falcor/Scene/Importers/AssimpImporter.cpp | 1061 ++++ .../Falcor/Scene/Importers/AssimpImporter.h | 45 + .../Falcor/Scene/Importers}/SceneImporter.cpp | 870 +-- .../Falcor/Scene/Importers/SceneImporter.h | 17 +- .../Falcor/Scene/Lights}/Light.cpp | 410 +- .../Falcor/Scene/Lights}/Light.h | 194 +- .../Falcor/Scene/Lights}/LightProbe.cpp | 102 +- .../Falcor/Scene/Lights}/LightProbe.h | 27 +- .../Falcor/Scene}/Material/Material.cpp | 59 +- .../Falcor/Scene}/Material/Material.h | 60 +- .../Scene}/ParticleSystem/ParticleSystem.cpp | 123 +- .../Scene}/ParticleSystem/ParticleSystem.h | 20 +- Source/Falcor/Scene/Scene.cpp | 950 ++++ Source/Falcor/Scene/Scene.h | 458 ++ Source/Falcor/Scene/SceneBuilder.cpp | 451 ++ Source/Falcor/Scene/SceneBuilder.h | 230 + Source/Falcor/ShaderSource.targets | 37 + Source/Falcor/ShaderSource.xml | 39 + .../Falcor}/ShadingUtils/BRDF.slang | 21 +- .../Falcor}/ShadingUtils/Helpers.slang | 174 +- .../Falcor}/ShadingUtils/Lights.slang | 39 +- Source/Falcor/ShadingUtils/Raytracing.slang | 121 + Source/Falcor/ShadingUtils/Scene.slang | 337 ++ .../Falcor}/ShadingUtils/Shading.slang | 132 +- Source/Falcor/ShadingUtils/Skinning.slang | 149 + .../Falcor/Testing}/UnitTest.cpp | 80 +- .../Falcor/Testing}/UnitTest.h | 277 +- Source/Falcor/Utils/Algorithm/BitonicSort.cpp | 102 + .../Utils/Algorithm/BitonicSort.cs.slang | 170 + Source/Falcor/Utils/Algorithm/BitonicSort.h | 75 + .../Algorithm/ComputeParallelReduction.cpp | 201 + .../Algorithm/ComputeParallelReduction.h | 94 + .../Falcor/Utils/Algorithm}/DirectedGraph.h | 5 +- .../Utils/Algorithm}/DirectedGraphTraversal.h | 6 +- .../Utils/Algorithm}/ParallelReduction.cpp | 53 +- .../Algorithm/ParallelReduction.cs.slang | 112 + .../Utils/Algorithm}/ParallelReduction.h | 22 +- .../Utils/Algorithm/ParallelReductionType.h | 34 + Source/Falcor/Utils/Algorithm/PrefixSum.cpp | 155 + .../Falcor/Utils/Algorithm/PrefixSum.cs.slang | 150 + Source/Falcor/Utils/Algorithm/PrefixSum.h | 74 + Source/Falcor/Utils/AlignedAllocator.h | 155 + .../Falcor/Utils}/ArgList.cpp | 30 +- .../Source => Source/Falcor/Utils}/ArgList.h | 9 +- .../Falcor}/Utils/BinaryFileStream.h | 4 +- .../Falcor/Utils/Color/ColorMap.slang | 58 +- Source/Falcor/Utils/Color/ColorUtils.h | 217 + Source/Falcor/Utils/Debug/DebugConsole.h | 112 + Source/Falcor/Utils/Debug/PixelDebug.cpp | 237 + Source/Falcor/Utils/Debug/PixelDebug.h | 97 + Source/Falcor/Utils/Debug/PixelDebug.slang | 122 + .../Falcor/Utils/Debug/PixelDebugTypes.h | 36 +- .../Falcor/Utils/Image}/Bitmap.cpp | 137 +- .../Falcor/Utils/Image}/Bitmap.h | 12 +- .../Falcor/Utils/Image}/DDSHeader.h | 7 +- .../Falcor/Utils/Image}/DXHeader.cpp | 17 +- .../Falcor/Utils/Image}/DXHeader.h | 2 +- Source/Falcor/Utils/Logger.cpp | 167 + .../Source => Source/Falcor}/Utils/Logger.h | 47 +- .../Utils => Source/Falcor/Utils/Math}/AABB.h | 8 +- Source/Falcor/Utils/Math/AABB.slang | 169 + Source/Falcor/Utils/Math/BBox.h | 97 + Source/Falcor/Utils/Math/BitTricks.slang | 120 + .../Falcor}/Utils/Math/CubicSpline.h | 2 +- .../Falcor}/Utils/Math/FalcorMath.h | 5 +- .../Falcor/Utils/Math/FormatConversion.slang | 66 +- Source/Falcor/Utils/Math/HalfUtils.slang | 74 + Source/Falcor/Utils/Math/HashUtils.slang | 71 + Source/Falcor/Utils/Math/MathConstants.slang | 107 + Source/Falcor/Utils/Math/MathHelpers.slang | 572 ++ .../Falcor/Utils/Math/PackedFormats.slang | 66 +- .../Utils/Math/SphericalHarmonics.slang | 85 + Source/Falcor/Utils/Math/Vector.h | 37 + .../Falcor/Utils/Perception}/Experiment.cpp | 9 +- .../Falcor/Utils/Perception}/Experiment.h | 11 +- .../SingleThresholdMeasurement.cpp | 29 +- .../Perception}/SingleThresholdMeasurement.h | 20 +- .../SampleGenerators/CPUSampleGenerator.h | 57 + .../SampleGenerators}/DxSamplePattern.cpp | 10 +- .../Utils/SampleGenerators}/DxSamplePattern.h | 17 +- .../SampleGenerators}/HaltonSamplePattern.cpp | 11 +- .../SampleGenerators}/HaltonSamplePattern.h | 18 +- .../StratifiedSamplePattern.cpp | 87 + .../StratifiedSamplePattern.h | 47 +- .../Utils/Sampling/Pseudorandom/LCG.slang | 64 + .../Sampling/Pseudorandom/SplitMix64.slang | 76 +- .../Utils/Sampling/Pseudorandom/Xoshiro.slang | 114 + .../Falcor/Utils/Sampling/SampleGenerator.cpp | 30 +- .../Falcor/Utils/Sampling/SampleGenerator.h | 56 +- .../Utils/Sampling/SampleGenerator.slang | 103 + .../Sampling/SampleGeneratorInterface.slang | 16 +- .../Utils/Sampling/SampleGeneratorType.h | 31 + .../Sampling/TinyUniformSampleGenerator.slang | 62 + .../Sampling/UniformSampleGenerator.slang | 73 + Source/Falcor/Utils/Scripting/Console.cpp | 120 + .../Falcor/Utils/Scripting/Console.h | 20 +- .../Falcor/Utils/Scripting}/Dictionary.h | 3 +- .../Falcor/Utils/Scripting/ScriptBindings.cpp | 83 + .../Falcor/Utils/Scripting/ScriptBindings.h | 250 + .../Falcor}/Utils/Scripting/Scripting.cpp | 73 +- .../Falcor}/Utils/Scripting/Scripting.h | 63 +- .../Falcor}/Utils/StringUtils.h | 43 +- Source/Falcor/Utils/Threading.cpp | 87 + .../Falcor/Utils/Threading.h | 62 +- Source/Falcor/Utils/Timing/Clock.cpp | 279 + Source/Falcor/Utils/Timing/Clock.h | 166 + .../Falcor/Utils/Timing}/CpuTimer.h | 12 +- .../Falcor/Utils/Timing/FrameRate.cpp | 24 +- .../Falcor/Utils/Timing}/FrameRate.h | 50 +- .../Falcor/Utils/Timing}/Profiler.cpp | 140 +- .../Falcor/Utils/Timing}/Profiler.h | 55 +- .../Falcor/Utils/UI}/DebugDrawer.cpp | 209 +- .../Falcor/Utils/UI}/DebugDrawer.h | 18 +- .../Utils => Source/Falcor/Utils/UI}/Font.cpp | 12 +- .../Utils => Source/Falcor/Utils/UI}/Font.h | 8 +- .../Falcor/Utils/UI}/Gizmo.cpp | 13 +- .../Editor => Source/Falcor/Utils/UI}/Gizmo.h | 21 +- Source/Falcor/Utils/UI/Gui.cpp | 1463 +++++ Source/Falcor/Utils/UI/Gui.h | 530 ++ .../Falcor/Utils/UI}/Picking.cpp | 40 +- .../Falcor/Utils/UI}/Picking.h | 16 +- .../Falcor/Utils/UI}/PixelZoom.cpp | 14 +- .../Falcor/Utils/UI}/PixelZoom.h | 9 +- Source/Falcor/Utils/UI/TextRenderer.cpp | 204 + .../Falcor/Utils/UI/TextRenderer.h | 69 +- .../Falcor/Utils/UI}/UserInput.h | 7 +- .../Falcor}/Utils/Video/VideoEncoder.cpp | 296 +- .../Falcor}/Utils/Video/VideoEncoder.h | 33 +- Source/Falcor/Utils/Video/VideoEncoderUI.cpp | 110 + .../Falcor}/Utils/Video/VideoEncoderUI.h | 35 +- Source/Falcor/dependencies.xml | 49 + Source/Falcor/stdafx.cpp | 28 + Source/Falcor/stdafx.h | 29 + Source/Mogwai/Data/BSDFViewer.py | 15 + Source/Mogwai/Data/Config.py | 32 + .../Mogwai/Data/ForwardRenderer.py | 22 +- Source/Mogwai/Data/PathTracer.py | 30 + .../Extensions/Capture/CaptureTrigger.cpp | 203 + .../Extensions/Capture/CaptureTrigger.h | 72 + .../Extensions/Capture/FrameCapture.cpp | 151 + .../Mogwai/Extensions/Capture/FrameCapture.h | 51 + .../Extensions/Capture/VideoCapture.cpp | 219 + .../Mogwai/Extensions/Capture/VideoCapture.h | 63 + Source/Mogwai/Mogwai.cpp | 602 ++ Source/Mogwai/Mogwai.h | 184 + .../Mogwai/Mogwai.vcxproj | 47 +- Source/Mogwai/Mogwai.vcxproj.filters | 57 + Source/Mogwai/MogwaiScripting.cpp | 145 + Source/Mogwai/MogwaiSettings.cpp | 354 ++ .../Mogwai/MogwaiSettings.h | 78 +- Source/Mogwai/Testing/testCSM.py | 14 + Source/Mogwai/Testing/testFXAA.py | 20 + Source/Mogwai/Testing/testForwardRendering.py | 21 + Source/Mogwai/Testing/testGaussianBlur.py | 20 + Source/Mogwai/Testing/testSSAO.py | 21 + Source/Mogwai/Testing/testSVGF.py | 57 + Source/Mogwai/Testing/testTAA.py | 21 + Source/Mogwai/Testing/testToneMapping.py | 16 + Source/Mogwai/stdafx.cpp | 28 + Source/Mogwai/stdafx.h | 30 + .../AccumulatePass/Accumulate.cs.slang | 125 + .../AccumulatePass/AccumulatePass.cpp | 248 + .../AccumulatePass/AccumulatePass.h | 103 + .../AccumulatePass/AccumulatePass.vcxproj | 60 +- .../AccumulatePass.vcxproj.filters | 6 +- .../x64/Debug/AccumulatePass.log | 1 + Source/RenderPasses/BSDFViewer/BSDFViewer.cpp | 426 ++ .../BSDFViewer/BSDFViewer.cs.slang | 464 ++ Source/RenderPasses/BSDFViewer/BSDFViewer.h | 80 + .../BSDFViewer/BSDFViewer.vcxproj | 104 + .../BSDFViewer/BSDFViewer.vcxproj.filters | 13 + .../BSDFViewer/BSDFViewerParams.h | 114 + .../DebugPasses/ComparisonPass.cpp | 209 + .../RenderPasses/DebugPasses/ComparisonPass.h | 78 + .../DebugPasses/Data/Comparison.ps.slang | 87 + .../Data/InvalidPixelDetection.ps.slang | 26 +- .../DebugPasses/Data/SideBySide.ps.slang | 30 +- .../DebugPasses/Data/SplitScreen.ps.slang | 30 +- .../RenderPasses/DebugPasses/DebugPasses.cpp | 32 +- .../DebugPasses/DebugPasses.vcxproj | 55 +- .../DebugPasses/DebugPasses.vcxproj.filters | 58 + .../InvalidPixelDetectionPass.cpp | 91 + .../InvalidPixelDetectionPass.h | 31 +- .../SideBySidePass/SideBySidePass.cpp | 62 + .../SideBySidePass/SideBySidePass.h | 22 +- .../SideBySidePass/Testing/testSideBySide.py | 20 + .../SplitScreenPass/SplitScreenPass.cpp | 35 +- .../SplitScreenPass/SplitScreenPass.h | 34 +- .../Testing/testSplitScreen.py | 20 + .../ErrorMeasurePass/ErrorMeasurePass.cpp | 402 ++ .../ErrorMeasurePass/ErrorMeasurePass.h | 102 + .../ErrorMeasurePass/ErrorMeasurePass.vcxproj | 65 +- .../ErrorMeasurePass/ErrorMeasurer.cs.slang | 40 + Source/RenderPasses/GBuffer/GBuffer.cpp | 208 + Source/RenderPasses/GBuffer/GBuffer.h | 89 + Source/RenderPasses/GBuffer/GBuffer.vcxproj | 112 + .../GBuffer/GBuffer.vcxproj.filters | 19 + .../RenderPasses/GBuffer/GBufferHelpers.slang | 79 +- .../RenderPasses/GBuffer/GBufferParams.h | 37 +- Source/RenderPasses/GBuffer/GBufferRT.cpp | 213 + Source/RenderPasses/GBuffer/GBufferRT.h | 90 + Source/RenderPasses/GBuffer/GBufferRaster.cpp | 170 + .../RenderPasses/GBuffer}/GBufferRaster.h | 32 +- .../RenderPasses/GBuffer/RasterPrimary.slang | 102 + .../GBuffer/RaytracePrimary.rt.slang | 208 + .../GBuffer/Testing/test_rasterGbuffer.py | 20 + .../GBuffer/Testing/test_rayGBuffer.py | 20 + .../MinimalPathTracer/MinimalPathTracer.cpp | 306 + .../MinimalPathTracer/MinimalPathTracer.h | 116 + .../MinimalPathTracer.rt.slang | 427 ++ .../MinimalPathTracer.vcxproj | 60 +- .../MinimalPathTracer.vcxproj.filters | 9 +- .../PassLibraryTemplate.cpp | 51 + .../PassLibraryTemplate/PassLibraryTemplate.h | 37 + .../PassLibraryTemplate.vcxproj | 105 + .../PassLibraryTemplate.vcxproj.filters | 4 +- .../RenderPasses/PathTracer/LoadGBuffer.slang | 125 + Source/RenderPasses/PathTracer/Logging.cpp | 168 + Source/RenderPasses/PathTracer/Logging.h | 93 + Source/RenderPasses/PathTracer/Logging.slang | 57 + .../Megakernel/MegakernelPathTracer.cpp | 214 + .../Megakernel/MegakernelPathTracer.h | 61 +- .../PathTracer/Megakernel/PathTracer.rt.slang | 262 + .../PathTracer/Megakernel/PathTracer.slang | 280 + .../PathTracer/Megakernel/RayData.slang | 161 + Source/RenderPasses/PathTracer/PathData.slang | 125 + Source/RenderPasses/PathTracer/PathTracer.cpp | 642 +++ Source/RenderPasses/PathTracer/PathTracer.h | 130 + .../PathTracer/PathTracer.vcxproj | 85 +- .../PathTracer/PathTracer.vcxproj.filters | 39 + .../PathTracer/PathTracerHelpers.slang | 418 ++ .../PathTracer/PathTracerParams.h | 107 + .../PathTracer/StaticParams.slang | 63 + .../PixelInspector.cs.slang | 164 + .../PixelInspectorPass/PixelInspectorData.h | 132 + .../PixelInspectorPass/PixelInspectorPass.cpp | 352 ++ .../PixelInspectorPass/PixelInspectorPass.h | 72 + .../PixelInspectorPass.vcxproj | 106 + .../PixelInspectorPass.vcxproj.filters | 13 + .../RenderPasses/SVGFPass/SVGFAtrous.ps.slang | 143 + .../RenderPasses/SVGFPass/SVGFCommon.slang.h | 64 + .../SVGFPass/SVGFFilterMoments.ps.slang | 126 + .../SVGFPass/SVGFFinalModulate.ps.slang | 23 +- .../SVGFPackLinearZAndNormal.ps.slang | 31 +- Source/RenderPasses/SVGFPass/SVGFPass.cpp | 426 ++ Source/RenderPasses/SVGFPass/SVGFPass.h | 92 + .../RenderPasses/SVGFPass/SVGFPass.vcxproj | 62 +- .../SVGFPass/SVGFPass.vcxproj.filters | 17 + .../SVGFPass/SVGFReproject.ps.slang | 251 + .../TemporalDelayPass/TemporalDelayPass.cpp | 149 + .../TemporalDelayPass/TemporalDelayPass.h | 37 +- .../TemporalDelayPass.vcxproj | 23 +- .../TemporalDelayPass.vcxproj.filters | 9 +- .../Testing/testTemporalDelayPass.py | 22 + Source/RenderPasses/make_new_pass_project.py | 34 + .../Samples/CudaInterop/CopySurface.cu | 56 +- Source/Samples/CudaInterop/CopySurface.h | 29 + Source/Samples/CudaInterop/CudaInterop.cpp | 83 + Source/Samples/CudaInterop/CudaInterop.h | 48 + .../Samples/CudaInterop/CudaInterop.vcxproj | 83 +- Source/Samples/CudaInterop/FalcorCUDA.cpp | 214 + .../Samples/CudaInterop/FalcorCUDA.h | 39 +- Source/Samples/CudaInterop/FalcorCUDA.props | 16 + Source/Samples/CudaInterop/README.md | 7 + .../Samples}/HelloDXR/Data/HelloDXR.ps.hlsl | 19 +- .../Samples}/HelloDXR/Data/HelloDXR.rt.hlsl | 48 +- .../Samples}/HelloDXR/HelloDXR.cpp | 116 +- .../Samples}/HelloDXR/HelloDXR.h | 29 +- .../Samples}/HelloDXR/HelloDXR.vcxproj | 18 +- .../HelloDXR/HelloDXR.vcxproj.filters | 0 .../ModelViewer/Data/ModelViewer.ps.hlsl | 25 +- Source/Samples/ModelViewer/ModelViewer.cpp | 255 + .../Samples}/ModelViewer/ModelViewer.h | 57 +- .../Samples}/ModelViewer/ModelViewer.vcxproj | 18 +- .../ModelViewer/ModelViewer.vcxproj.filters | 0 .../ProjectTemplate/ProjectTemplate.cpp | 28 +- .../Samples/ProjectTemplate/ProjectTemplate.h | 46 + .../ProjectTemplate/ProjectTemplate.vcxproj | 12 +- .../ProjectTemplate.vcxproj.filters | 0 .../Samples}/ShaderToy/Data/toy.hlsl | 2 +- .../Samples}/ShaderToy/Data/toyContainer.hlsl | 2 +- .../Samples}/ShaderToy/ShaderToy.cpp | 35 +- .../Samples}/ShaderToy/ShaderToy.h | 23 +- .../Samples}/ShaderToy/ShaderToy.vcxproj | 18 +- .../ShaderToy/ShaderToy.vcxproj.filters | 0 .../Samples}/make_new_project.py | 2 +- .../Tools}/FalcorTest/FalcorTest.cpp | 23 +- .../Tools}/FalcorTest/FalcorTest.h | 6 +- .../Tools}/FalcorTest/FalcorTest.vcxproj | 50 +- .../FalcorTest/FalcorTest.vcxproj.filters | 127 + .../FalcorTest/Tests/Core/BufferTests.cpp | 169 + .../Tests/Core/BufferTests.cs.slang | 73 +- .../InvalidPixelDetectionTests.cpp | 69 + .../Tests/Sampling/PseudorandomTests.cpp | 187 + .../Tests/Sampling/PseudorandomTests.cs.slang | 80 +- .../Tests/Sampling/SampleGeneratorTests.cpp | 166 + .../Sampling/SampleGeneratorTests.cs.slang | 75 + .../FalcorTest/Tests/Scene/EnvProbeTests.cpp | 62 + .../Tests/ShadingUtils/RaytracingTests.cpp | 106 + .../ShadingUtils/RaytracingTests.cs.slang | 29 +- .../Tests/ShadingUtils/ShadingUtilsTests.cpp | 289 + .../ShadingUtils}/ShadingUtilsTests.cs.slang | 29 +- .../FalcorTest/Tests/Slang/SlangShared.h | 62 + .../FalcorTest/Tests/Slang/SlangTests.cpp | 182 + .../Tests/Slang/SlangTests.cs.slang | 228 + .../FalcorTest/Tests/Utils/AABBTests.cpp | 157 + .../FalcorTest/Tests/Utils/AABBTests.cs.slang | 225 + .../Tests/Utils/AlignedAllocatorTests.cpp | 90 + .../FalcorTest/Tests/Utils/BitTricksTests.cpp | 90 + .../Tests/Utils/BitTricksTests.cs.slang | 59 +- .../Tests/Utils/BitonicSortTests.cpp | 97 + .../Tests/Utils/ColorUtilsTests.cpp | 112 + .../FalcorTest/Tests/Utils/HalfUtilsTests.cpp | 346 ++ .../Tests/Utils/HalfUtilsTests.cs.slang | 79 +- .../FalcorTest/Tests/Utils/HashUtilsTests.cpp | 112 + .../Tests/Utils/HashUtilsTests.cs.slang | 50 + .../Tests/Utils/MathHelpersTests.cpp | 268 + .../Tests/Utils/MathHelpersTests.cs.slang | 40 +- .../Tests/Utils/ParallelReductionTests.cpp | 251 + .../FalcorTest/Tests/Utils/PrefixSumTests.cpp | 112 + .../Data/DefaultPassIcon.png | Bin .../RenderGraphEditor/RenderGraphEditor.cpp | 329 +- .../RenderGraphEditor/RenderGraphEditor.h | 16 +- .../RenderGraphEditor.vcxproj | 68 +- Tests/BuildSolution.bat | 9 +- Tests/CompareOutput.py | 141 +- Tests/GraphTests.py | 130 + Tests/Helpers.py | 235 +- Tests/InternalConfig.py | 64 - Tests/MachineConfigs.py | 20 +- Tests/{ => Old}/CloneRepo.py | 0 Tests/{ => Old}/CollectAndEmailResults.py | 0 Tests/{ => Old}/GenerateReferences.py | 47 +- Tests/{ => Old}/GetBuildStatus.py | 10 +- Tests/{ => Old}/RemoveDirectoryTree.bat | 0 Tests/Old/RunAllTests.py | 208 + Tests/{ => Old}/RunPassTests.py | 253 +- Tests/{ => Old}/StartBuildTest.py | 29 +- Tests/Old/TeamCityCommon.py | 70 + Tests/{ => Old}/WriteTestResultsToHTML.py | 2 +- Tests/ReadMe.txt | 14 - Tests/TeamCityCommon.py | 25 - Tests/TestConfig.py | 25 + Tests/TestFalcor.py | 83 + Tests/build.xml | 9 - {Framework/Documentation => Tools}/Doxyfile | 0 .../SceneScripts => Tools}/buildTiledScene.py | 0 dependencies.xml | 50 - update_dependencies.bat | 4 - 916 files changed, 55089 insertions(+), 56113 deletions(-) create mode 100644 Build/deploycommon.bat create mode 100644 Build/deployproject.bat rename {packman => Build/packman}/config.packman.xml (100%) rename {packman => Build/packman}/packman (100%) mode change 100755 => 100644 rename {packman => Build/packman}/packman.cmd (100%) rename {packman => Build/packman}/win-bootstrap/configure.bat (100%) rename {packman => Build/packman}/win-bootstrap/fetch_file_from_s3.cmd (100%) rename {packman => Build/packman}/win-bootstrap/fetch_file_from_s3.ps1 (100%) rename {packman => Build/packman}/win-bootstrap/fetch_file_from_url.ps1 (100%) rename {packman => Build/packman}/win-bootstrap/generate_temp_file_name.ps1 (100%) rename {packman => Build/packman}/win-bootstrap/generate_temp_folder.ps1 (100%) rename {packman => Build/packman}/win-bootstrap/install_package.py (100%) create mode 100644 Build/patchpropssheet.py rename {Framework/BuildScripts => Build}/prebuild.bat (73%) create mode 100644 Build/update_dependencies.bat rename update_dependencies.sh => Build/update_dependencies.sh (100%) mode change 100755 => 100644 rename Framework/Documentation/CodeConvention.cpp => CodingConvention.cpp (84%) delete mode 100644 Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet.exe delete mode 100644 Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.cpp delete mode 100644 Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.filters delete mode 100644 Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.sln delete mode 100644 Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.vcxproj delete mode 100644 Framework/BuildScripts/movedata.bat delete mode 100644 Framework/BuildScripts/moveprojectdata.bat delete mode 100644 Framework/BuildScripts/postbuild.bat delete mode 100644 Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj delete mode 100644 Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj.filters delete mode 100644 Framework/Source/API/Buffer.cpp delete mode 100644 Framework/Source/API/ComputeContext.cpp delete mode 100644 Framework/Source/API/D3D12/D3D12ResourceViews.cpp delete mode 100644 Framework/Source/API/D3D12/D3DViews.h delete mode 100644 Framework/Source/API/FBO.cpp delete mode 100644 Framework/Source/API/Texture.cpp delete mode 100644 Framework/Source/Effects/AmbientOcclusion/SSAO.cpp delete mode 100644 Framework/Source/Effects/AmbientOcclusion/SSAO.h delete mode 100644 Framework/Source/Effects/FXAA/FXAA.cpp delete mode 100644 Framework/Source/Effects/NormalMap/LeanMap.cpp delete mode 100644 Framework/Source/Effects/SkyBox/SkyBox.cpp delete mode 100644 Framework/Source/Effects/SkyBox/SkyBox.h delete mode 100644 Framework/Source/Effects/TAA/TAA.cpp delete mode 100644 Framework/Source/Effects/TAA/TAA.h delete mode 100644 Framework/Source/Effects/ToneMapping/ToneMapping.cpp delete mode 100644 Framework/Source/Effects/ToneMapping/ToneMapping.h delete mode 100644 Framework/Source/Effects/Utils/GaussianBlur.cpp delete mode 100644 Framework/Source/Effects/Utils/GaussianBlur.h delete mode 100644 Framework/Source/Experimental/Raytracing/RtModel.cpp delete mode 100644 Framework/Source/Experimental/Raytracing/RtScene.cpp delete mode 100644 Framework/Source/Experimental/Raytracing/RtScene.h delete mode 100644 Framework/Source/Experimental/Raytracing/RtSceneRenderer.cpp delete mode 100644 Framework/Source/Experimental/Raytracing/RtSceneRenderer.h delete mode 100644 Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.cpp delete mode 100644 Framework/Source/Experimental/RenderGraph/RenderGraphScripting.cpp delete mode 100644 Framework/Source/Experimental/RenderGraph/RenderPassReflection.cpp delete mode 100644 Framework/Source/Experimental/RenderGraph/ResourceCache.cpp delete mode 100644 Framework/Source/Experimental/RenderPasses/ImageLoader.cpp delete mode 100644 Framework/Source/Falcor.h delete mode 100644 Framework/Source/Falcor.props delete mode 100644 Framework/Source/Falcor.vcxproj delete mode 100644 Framework/Source/Falcor.vcxproj.filters delete mode 100644 Framework/Source/Graphics/FboHelper.cpp delete mode 100644 Framework/Source/Graphics/FboHelper.h delete mode 100644 Framework/Source/Graphics/FullScreenPass.cpp delete mode 100644 Framework/Source/Graphics/FullScreenPass.h delete mode 100644 Framework/Source/Graphics/Model/Animation.cpp delete mode 100644 Framework/Source/Graphics/Model/Animation.h delete mode 100644 Framework/Source/Graphics/Model/AnimationController.cpp delete mode 100644 Framework/Source/Graphics/Model/AnimationController.h delete mode 100644 Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.cpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.h delete mode 100644 Framework/Source/Graphics/Model/Loaders/BinaryImage.cpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/BinaryImage.hpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.cpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.h delete mode 100644 Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.cpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/BinaryModelSpec.h delete mode 100644 Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.cpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.h delete mode 100644 Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.cpp delete mode 100644 Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.hpp delete mode 100644 Framework/Source/Graphics/Model/Mesh.cpp delete mode 100644 Framework/Source/Graphics/Model/Mesh.h delete mode 100644 Framework/Source/Graphics/Model/Model.cpp delete mode 100644 Framework/Source/Graphics/Model/Model.h delete mode 100644 Framework/Source/Graphics/Model/ObjectInstance.h delete mode 100644 Framework/Source/Graphics/Model/SkinningCache.cpp delete mode 100644 Framework/Source/Graphics/Model/SkinningCache.h delete mode 100644 Framework/Source/Graphics/Paths/ObjectPath.cpp delete mode 100644 Framework/Source/Graphics/Paths/ObjectPath.h delete mode 100644 Framework/Source/Graphics/Paths/PathEditor.cpp delete mode 100644 Framework/Source/Graphics/Paths/PathEditor.h delete mode 100644 Framework/Source/Graphics/Scene/Editor/SceneEditor.cpp delete mode 100644 Framework/Source/Graphics/Scene/Editor/SceneEditor.h delete mode 100644 Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.cpp delete mode 100644 Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.h delete mode 100644 Framework/Source/Graphics/Scene/Scene.cpp delete mode 100644 Framework/Source/Graphics/Scene/Scene.h delete mode 100644 Framework/Source/Graphics/Scene/SceneExportImportCommon.h delete mode 100644 Framework/Source/Graphics/Scene/SceneExporter.cpp delete mode 100644 Framework/Source/Graphics/Scene/SceneExporter.h delete mode 100644 Framework/Source/Graphics/Scene/SceneImporter.h delete mode 100644 Framework/Source/Graphics/Scene/SceneRenderer.cpp delete mode 100644 Framework/Source/Graphics/Scene/SceneRenderer.h delete mode 100644 Framework/Source/Sample.cpp delete mode 100644 Framework/Source/Sample.h delete mode 100644 Framework/Source/ShadingUtils/Raytracing.slang delete mode 100644 Framework/Source/ShadingUtils/ShadingUtilsTests.cpp delete mode 100644 Framework/Source/Utils/Gui.cpp delete mode 100644 Framework/Source/Utils/Gui.h delete mode 100644 Framework/Source/Utils/Logger.cpp delete mode 100644 Framework/Source/Utils/PythonEmbedding.cpp delete mode 100644 Framework/Source/Utils/PythonEmbedding.h delete mode 100644 Framework/Source/Utils/Scripting/ScriptBindings.cpp delete mode 100644 Framework/Source/Utils/TextRenderer.cpp delete mode 100644 Framework/Source/Utils/TextRenderer.h delete mode 100644 Framework/Source/Utils/Video/VideoDecoder.cpp delete mode 100644 Framework/Source/Utils/Video/VideoDecoder.h delete mode 100644 Framework/Source/Utils/Video/VideoEncoderUI.cpp delete mode 100644 Framework/Source/VR/OpenVR/VRController.cpp delete mode 100644 Framework/Source/VR/OpenVR/VRController.h delete mode 100644 Framework/Source/VR/OpenVR/VRDisplay.cpp delete mode 100644 Framework/Source/VR/OpenVR/VRDisplay.h delete mode 100644 Framework/Source/VR/OpenVR/VRPlayArea.h delete mode 100644 Framework/Source/VR/OpenVR/VRSystem.cpp delete mode 100644 Framework/Source/VR/OpenVR/VRSystem.h delete mode 100644 Framework/Source/VR/OpenVR/VRTrackerBox.cpp delete mode 100644 Framework/Source/VR/OpenVR/VRTrackerBox.h delete mode 100644 Framework/Source/VR/VrFbo.cpp create mode 100644 Mogwai.sln delete mode 100644 Samples/Core/ComputeShader/ComputeShader.cpp delete mode 100644 Samples/Core/ComputeShader/ComputeShader.vcxproj.filters delete mode 100644 Samples/Core/LearningWithEmbeddedPython/Data/RenderForLearning.slang delete mode 100644 Samples/Core/LearningWithEmbeddedPython/Data/demo_infer.py delete mode 100644 Samples/Core/LearningWithEmbeddedPython/Data/demo_init.py delete mode 100644 Samples/Core/LearningWithEmbeddedPython/Data/demo_train.py delete mode 100644 Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj delete mode 100644 Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj.filters delete mode 100644 Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython_overloadLoadScene.cpp delete mode 100644 Samples/Core/LearningWithEmbeddedPython/README.txt delete mode 100644 Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.cpp delete mode 100644 Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.h delete mode 100644 Samples/Core/MultiPassPostProcess/MultiPassPostProcess.cpp delete mode 100644 Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj delete mode 100644 Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj.filters delete mode 100644 Samples/Core/ShaderBuffers/Data/teapot.obj delete mode 100644 Samples/Core/ShaderBuffers/ShaderBuffers.cpp delete mode 100644 Samples/Core/ShaderBuffers/ShaderBuffers.h delete mode 100644 Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj.filters delete mode 100644 Samples/Core/SimpleDeferred/Data/LightingPass.ps.hlsl delete mode 100644 Samples/Core/SimpleDeferred/SimpleDeferred.cpp delete mode 100644 Samples/Core/SimpleDeferred/SimpleDeferred.h delete mode 100644 Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj.filters delete mode 100644 Samples/Core/StereoRendering/Data/StereoRendering.ps.hlsl delete mode 100644 Samples/Core/StereoRendering/StereoRendering.cpp delete mode 100644 Samples/Core/StereoRendering/StereoRendering.h delete mode 100644 Samples/Core/StereoRendering/StereoRendering.vcxproj.filters delete mode 100644 Samples/Effects/AmbientOcclusion/AmbientOcclusion.cpp delete mode 100644 Samples/Effects/AmbientOcclusion/AmbientOcclusion.h delete mode 100644 Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj delete mode 100644 Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj.filters delete mode 100644 Samples/Effects/HDRToneMapping/HDRToneMapping.cpp delete mode 100644 Samples/Effects/HDRToneMapping/HDRToneMapping.filters delete mode 100644 Samples/Effects/HDRToneMapping/HDRToneMapping.h delete mode 100644 Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj.filters delete mode 100644 Samples/Effects/HashedAlpha/HashedAlpha.cpp delete mode 100644 Samples/Effects/HashedAlpha/HashedAlpha.vcxproj delete mode 100644 Samples/Effects/HashedAlpha/HashedAlpha.vcxproj.filters delete mode 100644 Samples/Effects/Particles/Particles.cpp delete mode 100644 Samples/Effects/Particles/Particles.h delete mode 100644 Samples/Effects/Particles/Particles.vcxproj delete mode 100644 Samples/Effects/Particles/Particles.vcxproj.filters delete mode 100644 Samples/Effects/Shadows/Shadows.cpp delete mode 100644 Samples/Effects/Shadows/Shadows.h delete mode 100644 Samples/Effects/Shadows/Shadows.vcxproj delete mode 100644 Samples/Effects/Shadows/Shadows.vcxproj.filters delete mode 100644 Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.cpp delete mode 100644 Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.h delete mode 100644 Samples/ForwardRenderer/Data/ApplyAO.ps.slang delete mode 100644 Samples/ForwardRenderer/Data/ForwardRenderer.slang delete mode 100644 Samples/ForwardRenderer/ForwardRenderer.cpp delete mode 100644 Samples/ForwardRenderer/ForwardRenderer.h delete mode 100644 Samples/ForwardRenderer/ForwardRenderer.vcxproj.filters delete mode 100644 Samples/ForwardRenderer/ForwardRendererControls.cpp delete mode 100644 Samples/ForwardRenderer/ForwardRendererSceneRenderer.cpp delete mode 100644 Samples/ForwardRenderer/ForwardRendererSceneRenderer.h delete mode 100644 Samples/Raytracing/PathTracer/Data/GGXGICommon.slang delete mode 100644 Samples/Raytracing/PathTracer/Data/GGXGIRayGen.slang delete mode 100644 Samples/Raytracing/PathTracer/PathTracer.cpp delete mode 100644 Samples/Raytracing/PathTracer/PathTracer.vcxproj.filters delete mode 100644 Samples/Raytracing/PathTracer/RenderPasses/GBuffer.h delete mode 100644 Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.cpp delete mode 100644 Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.cpp delete mode 100644 Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.h delete mode 100644 Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.cpp delete mode 100644 Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.h delete mode 100644 Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.vcxproj delete mode 100644 Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.cpp delete mode 100644 Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.h delete mode 100644 Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj.filters delete mode 100644 Samples/RenderGraph/RenderGraphViewer/Testing/testAnimation.py delete mode 100644 Samples/RenderGraph/RenderGraphViewer/Testing/testForwardLighting.py delete mode 100644 Samples/RenderGraph/RenderGraphViewer/Testing/testToneMapping.py delete mode 100644 Samples/RenderGraph/SamplePassLibrary/Data/forward_renderer_with_dll.py delete mode 100644 Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.cpp delete mode 100644 Samples/Utils/FalcorTest/FalcorTest.vcxproj.Filters delete mode 100644 Samples/Utils/FalcorTest/Tests/ShadingUtilsTests.cpp delete mode 100644 Samples/Utils/LightProbeViewer/Data/UnitSphere.fbx delete mode 100644 Samples/Utils/LightProbeViewer/LightProbeViewer.cpp delete mode 100644 Samples/Utils/LightProbeViewer/LightProbeViewer.h delete mode 100644 Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj delete mode 100644 Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj.filters delete mode 100644 Samples/Utils/ModelViewer/ModelViewer.cpp delete mode 100644 Samples/Utils/SceneEditor/Data/SceneEditorApp.slang delete mode 100644 Samples/Utils/SceneEditor/SceneEditor.vcxproj delete mode 100644 Samples/Utils/SceneEditor/SceneEditor.vcxproj.filters delete mode 100644 Samples/Utils/SceneEditor/SceneEditorApp.cpp delete mode 100644 Samples/Utils/SceneEditor/SceneEditorApp.h rename {Framework => Source}/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp (99%) rename {Framework => Source}/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h (100%) create mode 100644 Source/Externals/mikktspace/README.md create mode 100644 Source/Externals/mikktspace/mikktspace.c create mode 100644 Source/Externals/mikktspace/mikktspace.h create mode 100644 Source/Externals/xoshiro/README.txt create mode 100644 Source/Externals/xoshiro/splitmix64.c create mode 100644 Source/Externals/xoshiro/xoshiro128starstar.c rename {Framework/Source => Source/Falcor/Core}/API/BlendState.cpp (94%) rename {Framework/Source => Source/Falcor/Core}/API/BlendState.h (98%) create mode 100644 Source/Falcor/Core/API/Buffer.cpp rename {Framework/Source => Source/Falcor/Core}/API/Buffer.h (67%) create mode 100644 Source/Falcor/Core/API/ComputeContext.cpp rename {Framework/Source => Source/Falcor/Core}/API/ComputeContext.h (58%) rename {Framework/Source => Source/Falcor/Core}/API/ComputeStateObject.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/ComputeStateObject.h (85%) rename {Framework/Source => Source/Falcor/Core}/API/CopyContext.cpp (80%) rename {Framework/Source => Source/Falcor/Core}/API/CopyContext.h (90%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12ApiData.h (94%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Buffer.cpp (79%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12ComputeContext.cpp (53%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12ComputeStateObject.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12CopyContext.cpp (95%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12DescriptorData.h (97%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12DescriptorHeap.cpp (83%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12DescriptorHeap.h (86%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12DescriptorPool.cpp (88%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12DescriptorSet.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Device.cpp (86%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Fbo.cpp (83%) rename Framework/Source/API/D3D12/D3DFormats.cpp => Source/Falcor/Core/API/D3D12/D3D12Formats.cpp (98%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12GpuFence.cpp (87%) rename Framework/Source/API/D3D12/LowLevel/D3D12ResourceAllocator.cpp => Source/Falcor/Core/API/D3D12/D3D12GpuMemoryHeap.cpp (58%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12GpuTimer.cpp (93%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12GraphicsStateObject.cpp (94%) rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12LowLevelContextData.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12NvApiExDesc.h (98%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12QueryHeap.cpp (94%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12RasterizerState.cpp (93%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12RenderContext.cpp (62%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Resource.cpp (92%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Resource.h (95%) create mode 100644 Source/Falcor/Core/API/D3D12/D3D12ResourceViews.cpp rename {Framework/Source/API/D3D12/LowLevel => Source/Falcor/Core/API/D3D12}/D3D12RootSignature.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Sampler.cpp (92%) rename Framework/Source/API/D3D12/D3DShader.cpp => Source/Falcor/Core/API/D3D12/D3D12Shader.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12State.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12State.h (94%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Texture.cpp (91%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/D3D12Vao.cpp (93%) rename {Framework/Source => Source/Falcor/Core}/API/D3D12/FalcorD3D12.h (97%) rename {Framework/Source => Source/Falcor/Core}/API/DepthStencilState.cpp (94%) rename {Framework/Source => Source/Falcor/Core}/API/DepthStencilState.h (93%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/DescriptorPool.cpp (90%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/DescriptorPool.h (90%) rename {Framework/Source => Source/Falcor/Core}/API/DescriptorSet.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/DescriptorSet.h (93%) rename {Framework/Source => Source/Falcor/Core}/API/Device.cpp (75%) rename {Framework/Source => Source/Falcor/Core}/API/Device.h (81%) create mode 100644 Source/Falcor/Core/API/FBO.cpp rename {Framework/Source => Source/Falcor/Core}/API/FBO.h (73%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/FencedPool.h (95%) rename {Framework/Source => Source/Falcor/Core}/API/Formats.cpp (54%) rename {Framework/Source => Source/Falcor/Core}/API/Formats.h (81%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/GpuFence.h (93%) rename Framework/Source/API/LowLevel/ResourceAllocator.cpp => Source/Falcor/Core/API/GpuMemoryHeap.cpp (82%) rename Framework/Source/API/LowLevel/ResourceAllocator.h => Source/Falcor/Core/API/GpuMemoryHeap.h (72%) rename {Framework/Source => Source/Falcor/Core}/API/GpuTimer.cpp (94%) rename {Framework/Source => Source/Falcor/Core}/API/GpuTimer.h (93%) rename {Framework/Source => Source/Falcor/Core}/API/GraphicsStateObject.cpp (94%) rename {Framework/Source => Source/Falcor/Core}/API/GraphicsStateObject.h (91%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/LowLevelContextData.h (93%) rename {Framework/Source => Source/Falcor/Core}/API/QueryHeap.h (95%) rename Framework/Source/Utils/PatternGenerators/PatternGenerator.h => Source/Falcor/Core/API/RasterizerState.cpp (80%) rename {Framework/Source => Source/Falcor/Core}/API/RasterizerState.h (91%) rename {Framework/Source => Source/Falcor/Core}/API/RenderContext.cpp (64%) rename {Framework/Source => Source/Falcor/Core}/API/RenderContext.h (68%) rename {Framework/Source => Source/Falcor/Core}/API/Resource.cpp (50%) rename {Framework/Source => Source/Falcor/Core}/API/Resource.h (69%) create mode 100644 Source/Falcor/Core/API/ResourceViews.cpp rename {Framework/Source => Source/Falcor/Core}/API/ResourceViews.h (64%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/RootSignature.cpp (94%) rename {Framework/Source/API/LowLevel => Source/Falcor/Core/API}/RootSignature.h (95%) rename {Framework/Source => Source/Falcor/Core}/API/Sampler.cpp (78%) rename {Framework/Source => Source/Falcor/Core}/API/Sampler.h (94%) rename {Framework/Source => Source/Falcor/Core}/API/Shader.h (92%) create mode 100644 Source/Falcor/Core/API/Texture.cpp rename {Framework/Source => Source/Falcor/Core}/API/Texture.h (75%) rename Framework/Source/Graphics/TextureHelper.cpp => Source/Falcor/Core/API/TextureLoader.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/VAO.cpp (91%) rename {Framework/Source => Source/Falcor/Core}/API/VAO.h (92%) rename Samples/Utils/LightProbeViewer/Data/LightProbeViewer.ps.hlsl => Source/Falcor/Core/API/VertexLayout.cpp (88%) rename {Framework/Source => Source/Falcor/Core}/API/VertexLayout.h (84%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/FalcorVK.h (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKBuffer.cpp (93%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKComputeContext.cpp (93%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKComputeStateObject.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKCopyContext.cpp (97%) rename {Framework/Source/API/Vulkan/LowLevel => Source/Falcor/Core/API/Vulkan}/VKDescriptorData.h (97%) rename {Framework/Source/API/Vulkan/LowLevel => Source/Falcor/Core/API/Vulkan}/VKDescriptorPool.cpp (95%) rename {Framework/Source/API/Vulkan/LowLevel => Source/Falcor/Core/API/Vulkan}/VKDescriptorSet.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKDevice.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKFbo.cpp (97%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKFormats.cpp (99%) rename {Framework/Source/API/Vulkan/LowLevel => Source/Falcor/Core/API/Vulkan}/VKGpuFence.cpp (99%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKGpuTimer.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKGraphicsStateObject.cpp (94%) rename {Framework/Source/API/Vulkan/LowLevel => Source/Falcor/Core/API/Vulkan}/VKLowLevelContextData.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKRasterizerState.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKRenderContext.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKResourceViews.cpp (99%) rename {Framework/Source/API/Vulkan/LowLevel => Source/Falcor/Core/API/Vulkan}/VKRootSignature.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKSampler.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKShader.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKSmartHandle.h (99%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKState.cpp (99%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKState.h (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKTexture.cpp (98%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VKVao.cpp (95%) rename Framework/Source/API/Vulkan/LowLevel/VKResourceAllocator.cpp => Source/Falcor/Core/API/Vulkan/VkGpuMemoryHeap.cpp (88%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VkQueryHeap.cpp (96%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VkResource.cpp (95%) rename {Framework/Source => Source/Falcor/Core}/API/Vulkan/VkSmartHandle.cpp (98%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/ConstantBuffer.cpp (81%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/ConstantBuffer.h (82%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/StructuredBuffer.cpp (76%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/StructuredBuffer.h (86%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/TypedBuffer.cpp (76%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/TypedBuffer.h (94%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/VariablesBuffer.cpp (83%) rename {Framework/Source/API => Source/Falcor/Core/BufferTypes}/VariablesBuffer.h (84%) rename {Framework/Source/Utils => Source/Falcor/Core/BufferTypes}/VariablesBufferUI.cpp (68%) rename {Framework/Source/Utils => Source/Falcor/Core/BufferTypes}/VariablesBufferUI.h (82%) rename {Framework/Source => Source/Falcor/Core}/FalcorConfig.h (85%) rename Framework/FalcorSharedObjects/FalcorSharedObjects.cpp => Source/Falcor/Core/Framework.cpp (75%) rename {Framework/Source => Source/Falcor/Core}/Framework.h (80%) rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/Linux/Linux.cpp (92%) rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/Linux/ProgressBarLinux.cpp (96%) rename {Framework/Source/Utils => Source/Falcor/Core/Platform}/MonitorInfo.cpp (98%) rename {Framework/Source/Utils => Source/Falcor/Core/Platform}/MonitorInfo.h (91%) rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/OS.cpp (93%) rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/OS.h (69%) create mode 100644 Source/Falcor/Core/Platform/ProgressBar.cpp rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/ProgressBar.h (84%) rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/Windows/ProgressBarWin.cpp (87%) rename {Framework/Source/Utils => Source/Falcor/Core}/Platform/Windows/Windows.cpp (83%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ComputeProgram.cpp (84%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ComputeProgram.h (82%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/GraphicsProgram.cpp (93%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/GraphicsProgram.h (86%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ParameterBlock.cpp (83%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ParameterBlock.h (86%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/Program.cpp (94%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/Program.h (92%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ProgramReflection.cpp (91%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ProgramReflection.h (96%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ProgramVars.cpp (89%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ProgramVars.h (87%) create mode 100644 Source/Falcor/Core/Program/ProgramVarsHelpers.h rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ProgramVersion.cpp (89%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ProgramVersion.h (91%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ShaderLibrary.cpp (95%) rename {Framework/Source/Graphics => Source/Falcor/Core}/Program/ShaderLibrary.h (94%) rename {Framework/Source => Source/Falcor/Core}/Renderer.h (50%) create mode 100644 Source/Falcor/Core/Sample.cpp create mode 100644 Source/Falcor/Core/Sample.h rename {Framework/Source/Graphics => Source/Falcor/Core/State}/ComputeState.cpp (91%) rename {Framework/Source/Graphics => Source/Falcor/Core/State}/ComputeState.h (92%) rename {Framework/Source/Graphics => Source/Falcor/Core/State}/GraphicsState.cpp (97%) rename {Framework/Source/Graphics => Source/Falcor/Core/State}/GraphicsState.h (96%) rename Framework/Source/Utils/Graph.h => Source/Falcor/Core/State/StateGraph.h (93%) rename {Framework/Source/API => Source/Falcor/Core}/Window.cpp (92%) rename {Framework/Source/API => Source/Falcor/Core}/Window.h (87%) rename {Framework/Source => Source/Falcor}/Data/ApplyAO.ps.slang (96%) rename {Framework/Source => Source/Falcor}/Data/Effects/CascadedShadowMap.slang (99%) rename {Framework/Source => Source/Falcor}/Data/Effects/CsmData.h (96%) rename {Framework/Source => Source/Falcor}/Data/Effects/DepthPass.slang (89%) rename {Framework/Source => Source/Falcor}/Data/Effects/FXAA.slang (99%) rename {Framework/Source => Source/Falcor}/Data/Effects/GaussianBlur.ps.slang (95%) rename {Framework/Source => Source/Falcor}/Data/Effects/LeanMapping.slang (96%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleConstColor.ps.slang (96%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleData.h (96%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleEmit.cs.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleInterpColor.ps.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleSimulate.cs.slang (98%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleSort.cs.slang (98%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleTexture.ps.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Effects/ParticleVertex.vs.slang (98%) rename {Framework/Source => Source/Falcor}/Data/Effects/SSAO.ps.slang (89%) rename {Framework/Source => Source/Falcor}/Data/Effects/SSAOData.h (92%) rename {Framework/Source => Source/Falcor}/Data/Effects/ShadowPass.slang (93%) rename {Framework/Source => Source/Falcor}/Data/Effects/SkyBox.slang (91%) rename {Framework/Source => Source/Falcor}/Data/Effects/TAA.ps.slang (98%) rename {Framework/Source => Source/Falcor}/Data/Effects/ToneMapping.ps.slang (82%) create mode 100644 Source/Falcor/Data/Effects/ToneMappingData.h rename {Framework/Source => Source/Falcor}/Data/Effects/VisibilityPass.ps.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Effects/cube.obj (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Fonts/DejaVu Sans Mono14.000000.bin (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Fonts/DejaVu Sans Mono14.000000.dds (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Fonts/consolab.ttf (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Fonts/trebucbd.ttf (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Models/Camera.obj (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Models/LightBulb.obj (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Models/RotateGizmo.obj (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Models/ScaleGizmo.obj (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Models/TranslateGizmo.obj (100%) rename {Framework/Source => Source/Falcor}/Data/Framework/Nvidia.ico (100%) rename Framework/Source/Data/Framework/Shaders/Blit.ps.slang => Source/Falcor/Data/Framework/Shaders/Blit.slang (83%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/ComputeSkinning.cs.slang (87%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/FullScreenPass.gs.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/FullScreenPass.vs.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/Gui.slang (97%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/LightProbeIntegration.ps.slang (81%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/MaterialBlock.slang (96%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/ParallelReduction.ps.slang (98%) rename Samples/Core/MultiPassPostProcess/Data/Blit.ps.hlsl => Source/Falcor/Data/Framework/Shaders/SceneBlock.slang (87%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/SceneEditor.slang (95%) rename {Framework/Source => Source/Falcor}/Data/Framework/Shaders/TextRenderer.slang (97%) create mode 100644 Source/Falcor/Data/Framework/Textures/NextFrame.jpg create mode 100644 Source/Falcor/Data/Framework/Textures/Pause.jpg create mode 100644 Source/Falcor/Data/Framework/Textures/Play.jpg create mode 100644 Source/Falcor/Data/Framework/Textures/PrevFrame.jpg create mode 100644 Source/Falcor/Data/Framework/Textures/Rewind.jpg create mode 100644 Source/Falcor/Data/Framework/Textures/Stop.jpg rename {Framework/Source => Source/Falcor}/Data/HostDeviceData.h (96%) rename {Framework/Source => Source/Falcor}/Data/HostDeviceData.slang (96%) rename {Framework/Source => Source/Falcor}/Data/HostDeviceSharedCode.h (73%) rename {Framework/Source => Source/Falcor}/Data/HostDeviceSharedMacros.h (96%) rename Framework/Source/Data/DefaultVS.slang => Source/Falcor/Data/Raster.slang (53%) rename {Samples/ForwardRenderer/Data => Source/Falcor/Data/RenderPasses}/DepthPass.ps.slang (85%) rename {Framework/Source => Source/Falcor}/Data/RenderPasses/ForwardLightingPass.slang (80%) rename {Framework/Source => Source/Falcor}/Data/ShaderCommon.slang (59%) rename {Framework/Source => Source/Falcor}/Data/UnitTest.cs.hlsl (100%) rename {Framework/Source => Source/Falcor}/Data/VertexAttrib.h (80%) create mode 100644 Source/Falcor/Effects/AmbientOcclusion/SSAOPass.cpp create mode 100644 Source/Falcor/Effects/AmbientOcclusion/SSAOPass.h create mode 100644 Source/Falcor/Effects/FXAA/FXAAPass.cpp rename Framework/Source/Effects/FXAA/FXAA.h => Source/Falcor/Effects/FXAA/FXAAPass.h (57%) rename {Framework/Source => Source/Falcor}/Effects/Shadows/CSM.cpp (62%) rename {Framework/Source => Source/Falcor}/Effects/Shadows/CSM.h (51%) create mode 100644 Source/Falcor/Effects/SkyBox/SkyBox.cpp create mode 100644 Source/Falcor/Effects/SkyBox/SkyBox.h create mode 100644 Source/Falcor/Effects/TAA/TAAPass.cpp rename Framework/Source/Experimental/Raytracing/RtModel.h => Source/Falcor/Effects/TAA/TAAPass.h (56%) create mode 100644 Source/Falcor/Effects/ToneMapping/ToneMappingPass.cpp create mode 100644 Source/Falcor/Effects/ToneMapping/ToneMappingPass.h create mode 100644 Source/Falcor/Effects/Utils/GaussianBlurPass.cpp create mode 100644 Source/Falcor/Effects/Utils/GaussianBlurPass.h create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveIntegrator.ps.slang rename Framework/Source/Utils/Platform/ProgressBar.cpp => Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.cpp (74%) create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.h rename Samples/Core/ComputeShader/Data/compute.hlsl => Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.slang (52%) create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerHelpers.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerInterface.slang rename Samples/Core/StereoRendering/Data/StereoRendering.vs.hlsl => Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerType.h (61%) create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.cpp create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.h create mode 100644 Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/EnvProbe.cpp create mode 100644 Source/Falcor/Experimental/Scene/Lights/EnvProbe.h create mode 100644 Source/Falcor/Experimental/Scene/Lights/EnvProbe.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/EnvProbeSetup.cs.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/LightCollection.cpp create mode 100644 Source/Falcor/Experimental/Scene/Lights/LightCollection.cs.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/LightCollection.h create mode 100644 Source/Falcor/Experimental/Scene/Lights/LightCollection.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/LightCollectionShared.h create mode 100644 Source/Falcor/Experimental/Scene/Lights/LightHelpers.slang create mode 100644 Source/Falcor/Experimental/Scene/Lights/MeshLightData.h create mode 100644 Source/Falcor/Experimental/Scene/Material/MaterialHelpers.slang create mode 100644 Source/Falcor/Experimental/Scene/Material/MaterialShading.slang create mode 100644 Source/Falcor/Falcor.h rename {Framework/Source => Source/Falcor}/Falcor.natvis (100%) create mode 100644 Source/Falcor/Falcor.props create mode 100644 Source/Falcor/Falcor.vcxproj create mode 100644 Source/Falcor/Falcor.vcxproj.filters create mode 100644 Source/Falcor/FalcorExperimental.h rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/HitProgram.cpp (97%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/HitProgram.h (98%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/RtProgram.cpp (97%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/RtProgram.h (86%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/RtProgramVersion.cpp (94%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/RtProgramVersion.h (89%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgram/SingleShaderProgram.h (97%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgramVars.cpp (74%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgramVars.h (85%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgramVarsHelper.cpp (90%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtProgramVarsHelper.h (96%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtShader.cpp (98%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtShader.h (92%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtState.cpp (97%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtState.h (93%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtStateObject.cpp (98%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtStateObject.h (87%) rename {Framework/Source/Experimental => Source/Falcor}/Raytracing/RtStateObjectHelper.h (99%) create mode 100644 Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp create mode 100644 Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h create mode 100644 Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp create mode 100644 Source/Falcor/RenderGraph/BasePasses/ComputePass.h create mode 100644 Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp create mode 100644 Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h rename Framework/Source/ShadingUtils/ShadingUtilsTests.cs.hlsl => Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp (57%) create mode 100644 Source/Falcor/RenderGraph/BasePasses/RasterPass.h create mode 100644 Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp create mode 100644 Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraph.cpp (53%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraph.h (80%) create mode 100644 Source/Falcor/RenderGraph/RenderGraphCompiler.cpp rename Framework/Source/Experimental/RenderGraph/RenderGraphScripting.h => Source/Falcor/RenderGraph/RenderGraphCompiler.h (55%) create mode 100644 Source/Falcor/RenderGraph/RenderGraphExe.cpp create mode 100644 Source/Falcor/RenderGraph/RenderGraphExe.h rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraphIR.cpp (65%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraphIR.h (81%) create mode 100644 Source/Falcor/RenderGraph/RenderGraphImportExport.cpp rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraphImportExport.h (75%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraphUI.cpp (84%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderGraphUI.h (87%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderPass.cpp (80%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderPass.h (51%) rename Framework/Source/Graphics/Model/ModelRenderer.cpp => Source/Falcor/RenderGraph/RenderPassHelpers.h (72%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderPassLibrary.cpp (83%) rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderPassLibrary.h (90%) create mode 100644 Source/Falcor/RenderGraph/RenderPassReflection.cpp rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/RenderPassReflection.h (68%) rename Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.h => Source/Falcor/RenderGraph/RenderPassStandardFlags.h (68%) create mode 100644 Source/Falcor/RenderGraph/ResourceCache.cpp rename {Framework/Source/Experimental => Source/Falcor}/RenderGraph/ResourceCache.h (70%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/BlitPass.cpp (76%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/BlitPass.h (77%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/DepthPass.cpp (76%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/DepthPass.h (74%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/ForwardLightingPass.cpp (75%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/ForwardLightingPass.h (78%) create mode 100644 Source/Falcor/RenderPasses/ImageLoader.cpp rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/ImageLoader.h (73%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/ResolvePass.cpp (87%) rename {Framework/Source/Experimental => Source/Falcor}/RenderPasses/ResolvePass.h (81%) create mode 100644 Source/Falcor/Scene/Animation/Animation.cpp create mode 100644 Source/Falcor/Scene/Animation/Animation.h create mode 100644 Source/Falcor/Scene/Animation/AnimationController.cpp create mode 100644 Source/Falcor/Scene/Animation/AnimationController.h rename {Framework/Source/Graphics => Source/Falcor/Scene}/Camera/Camera.cpp (72%) rename {Framework/Source/Graphics => Source/Falcor/Scene}/Camera/Camera.h (81%) rename {Framework/Source/Graphics => Source/Falcor/Scene}/Camera/CameraController.cpp (69%) rename {Framework/Source/Graphics => Source/Falcor/Scene}/Camera/CameraController.h (80%) create mode 100644 Source/Falcor/Scene/Importers/AssimpImporter.cpp create mode 100644 Source/Falcor/Scene/Importers/AssimpImporter.h rename {Framework/Source/Graphics/Scene => Source/Falcor/Scene/Importers}/SceneImporter.cpp (55%) rename Framework/Source/Utils/Scripting/ScriptBindings.h => Source/Falcor/Scene/Importers/SceneImporter.h (88%) rename {Framework/Source/Graphics => Source/Falcor/Scene/Lights}/Light.cpp (52%) rename {Framework/Source/Graphics => Source/Falcor/Scene/Lights}/Light.h (66%) rename {Framework/Source/Graphics => Source/Falcor/Scene/Lights}/LightProbe.cpp (73%) rename {Framework/Source/Graphics => Source/Falcor/Scene/Lights}/LightProbe.h (89%) rename {Framework/Source/Graphics => Source/Falcor/Scene}/Material/Material.cpp (90%) rename {Framework/Source/Graphics => Source/Falcor/Scene}/Material/Material.h (89%) rename {Framework/Source/Effects => Source/Falcor/Scene}/ParticleSystem/ParticleSystem.cpp (77%) rename {Framework/Source/Effects => Source/Falcor/Scene}/ParticleSystem/ParticleSystem.h (95%) create mode 100644 Source/Falcor/Scene/Scene.cpp create mode 100644 Source/Falcor/Scene/Scene.h create mode 100644 Source/Falcor/Scene/SceneBuilder.cpp create mode 100644 Source/Falcor/Scene/SceneBuilder.h create mode 100644 Source/Falcor/ShaderSource.targets create mode 100644 Source/Falcor/ShaderSource.xml rename {Framework/Source => Source/Falcor}/ShadingUtils/BRDF.slang (91%) rename {Framework/Source => Source/Falcor}/ShadingUtils/Helpers.slang (69%) rename {Framework/Source => Source/Falcor}/ShadingUtils/Lights.slang (86%) create mode 100644 Source/Falcor/ShadingUtils/Raytracing.slang create mode 100644 Source/Falcor/ShadingUtils/Scene.slang rename {Framework/Source => Source/Falcor}/ShadingUtils/Shading.slang (76%) create mode 100644 Source/Falcor/ShadingUtils/Skinning.slang rename {Framework/Source => Source/Falcor/Testing}/UnitTest.cpp (80%) rename {Framework/Source => Source/Falcor/Testing}/UnitTest.h (52%) create mode 100644 Source/Falcor/Utils/Algorithm/BitonicSort.cpp create mode 100644 Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang create mode 100644 Source/Falcor/Utils/Algorithm/BitonicSort.h create mode 100644 Source/Falcor/Utils/Algorithm/ComputeParallelReduction.cpp create mode 100644 Source/Falcor/Utils/Algorithm/ComputeParallelReduction.h rename {Framework/Source/Utils => Source/Falcor/Utils/Algorithm}/DirectedGraph.h (99%) rename {Framework/Source/Utils => Source/Falcor/Utils/Algorithm}/DirectedGraphTraversal.h (99%) rename {Framework/Source/Utils/Math => Source/Falcor/Utils/Algorithm}/ParallelReduction.cpp (74%) create mode 100644 Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang rename {Framework/Source/Utils/Math => Source/Falcor/Utils/Algorithm}/ParallelReduction.h (84%) create mode 100644 Source/Falcor/Utils/Algorithm/ParallelReductionType.h create mode 100644 Source/Falcor/Utils/Algorithm/PrefixSum.cpp create mode 100644 Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang create mode 100644 Source/Falcor/Utils/Algorithm/PrefixSum.h create mode 100644 Source/Falcor/Utils/AlignedAllocator.h rename {Framework/Source => Source/Falcor/Utils}/ArgList.cpp (71%) rename {Framework/Source => Source/Falcor/Utils}/ArgList.h (95%) rename {Framework/Source => Source/Falcor}/Utils/BinaryFileStream.h (98%) rename Samples/Raytracing/PathTracer/Data/RasterPrimary.slang => Source/Falcor/Utils/Color/ColorMap.slang (59%) create mode 100644 Source/Falcor/Utils/Color/ColorUtils.h create mode 100644 Source/Falcor/Utils/Debug/DebugConsole.h create mode 100644 Source/Falcor/Utils/Debug/PixelDebug.cpp create mode 100644 Source/Falcor/Utils/Debug/PixelDebug.h create mode 100644 Source/Falcor/Utils/Debug/PixelDebug.slang rename Samples/Core/MultiPassPostProcess/Data/Luminance.ps.hlsl => Source/Falcor/Utils/Debug/PixelDebugTypes.h (73%) rename {Framework/Source/Utils => Source/Falcor/Utils/Image}/Bitmap.cpp (77%) rename {Framework/Source/Utils => Source/Falcor/Utils/Image}/Bitmap.h (94%) rename {Framework/Source/Utils => Source/Falcor/Utils/Image}/DDSHeader.h (96%) rename {Framework/Source/Utils => Source/Falcor/Utils/Image}/DXHeader.cpp (94%) rename {Framework/Source/Utils => Source/Falcor/Utils/Image}/DXHeader.h (99%) create mode 100644 Source/Falcor/Utils/Logger.cpp rename {Framework/Source => Source/Falcor}/Utils/Logger.h (68%) rename {Framework/Source/Utils => Source/Falcor/Utils/Math}/AABB.h (97%) create mode 100644 Source/Falcor/Utils/Math/AABB.slang create mode 100644 Source/Falcor/Utils/Math/BBox.h create mode 100644 Source/Falcor/Utils/Math/BitTricks.slang rename {Framework/Source => Source/Falcor}/Utils/Math/CubicSpline.h (99%) rename {Framework/Source => Source/Falcor}/Utils/Math/FalcorMath.h (98%) rename Framework/Source/VR/OpenVR/VRPlayArea.cpp => Source/Falcor/Utils/Math/FormatConversion.slang (52%) create mode 100644 Source/Falcor/Utils/Math/HalfUtils.slang create mode 100644 Source/Falcor/Utils/Math/HashUtils.slang create mode 100644 Source/Falcor/Utils/Math/MathConstants.slang create mode 100644 Source/Falcor/Utils/Math/MathHelpers.slang rename Samples/Core/ShaderBuffers/Data/ShaderBuffers.hlsl => Source/Falcor/Utils/Math/PackedFormats.slang (59%) create mode 100644 Source/Falcor/Utils/Math/SphericalHarmonics.slang create mode 100644 Source/Falcor/Utils/Math/Vector.h rename {Framework/Source/Utils/Psychophysics => Source/Falcor/Utils/Perception}/Experiment.cpp (98%) rename {Framework/Source/Utils/Psychophysics => Source/Falcor/Utils/Perception}/Experiment.h (97%) rename {Framework/Source/Utils/Psychophysics => Source/Falcor/Utils/Perception}/SingleThresholdMeasurement.cpp (93%) rename {Framework/Source/Utils/Psychophysics => Source/Falcor/Utils/Perception}/SingleThresholdMeasurement.h (90%) create mode 100644 Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h rename {Framework/Source/Utils/PatternGenerators => Source/Falcor/Utils/SampleGenerators}/DxSamplePattern.cpp (88%) rename {Framework/Source/Utils/PatternGenerators => Source/Falcor/Utils/SampleGenerators}/DxSamplePattern.h (77%) rename {Framework/Source/Utils/PatternGenerators => Source/Falcor/Utils/SampleGenerators}/HaltonSamplePattern.cpp (83%) rename {Framework/Source/Utils/PatternGenerators => Source/Falcor/Utils/SampleGenerators}/HaltonSamplePattern.h (82%) create mode 100644 Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp rename Framework/Source/Graphics/Model/Loaders/ModelImporter.h => Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h (52%) create mode 100644 Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang rename Samples/Effects/Shadows/Data/Shadows.slang => Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang (51%) create mode 100644 Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang rename Framework/Source/Graphics/Model/Loaders/ModelImporter.cpp => Source/Falcor/Utils/Sampling/SampleGenerator.cpp (72%) rename Framework/Source/VR/VrFbo.h => Source/Falcor/Utils/Sampling/SampleGenerator.h (52%) create mode 100644 Source/Falcor/Utils/Sampling/SampleGenerator.slang rename Samples/Effects/HashedAlpha/Data/HashedAlpha.ps.hlsl => Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang (85%) create mode 100644 Source/Falcor/Utils/Sampling/SampleGeneratorType.h create mode 100644 Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang create mode 100644 Source/Falcor/Utils/Sampling/UniformSampleGenerator.slang create mode 100644 Source/Falcor/Utils/Scripting/Console.cpp rename Framework/Source/API/Vulkan/VKProgramVersion.cpp => Source/Falcor/Utils/Scripting/Console.h (86%) rename {Framework/Source/Utils => Source/Falcor/Utils/Scripting}/Dictionary.h (98%) create mode 100644 Source/Falcor/Utils/Scripting/ScriptBindings.cpp create mode 100644 Source/Falcor/Utils/Scripting/ScriptBindings.h rename {Framework/Source => Source/Falcor}/Utils/Scripting/Scripting.cpp (72%) rename {Framework/Source => Source/Falcor}/Utils/Scripting/Scripting.h (59%) rename {Framework/Source => Source/Falcor}/Utils/StringUtils.h (88%) create mode 100644 Source/Falcor/Utils/Threading.cpp rename Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.h => Source/Falcor/Utils/Threading.h (58%) create mode 100644 Source/Falcor/Utils/Timing/Clock.cpp create mode 100644 Source/Falcor/Utils/Timing/Clock.h rename {Framework/Source/Utils => Source/Falcor/Utils/Timing}/CpuTimer.h (91%) rename Framework/Source/API/D3D12/D3DProgramVersion.cpp => Source/Falcor/Utils/Timing/FrameRate.cpp (76%) rename {Framework/Source/Utils => Source/Falcor/Utils/Timing}/FrameRate.h (75%) rename {Framework/Source/Utils => Source/Falcor/Utils/Timing}/Profiler.cpp (54%) rename {Framework/Source/Utils => Source/Falcor/Utils/Timing}/Profiler.h (79%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/DebugDrawer.cpp (51%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/DebugDrawer.h (91%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/Font.cpp (94%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/Font.h (96%) rename {Framework/Source/Graphics/Scene/Editor => Source/Falcor/Utils/UI}/Gizmo.cpp (98%) rename {Framework/Source/Graphics/Scene/Editor => Source/Falcor/Utils/UI}/Gizmo.h (94%) create mode 100644 Source/Falcor/Utils/UI/Gui.cpp create mode 100644 Source/Falcor/Utils/UI/Gui.h rename {Framework/Source/Utils/Picking => Source/Falcor/Utils/UI}/Picking.cpp (86%) rename {Framework/Source/Utils/Picking => Source/Falcor/Utils/UI}/Picking.h (95%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/PixelZoom.cpp (90%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/PixelZoom.h (94%) create mode 100644 Source/Falcor/Utils/UI/TextRenderer.cpp rename Framework/Source/Effects/NormalMap/LeanMap.h => Source/Falcor/Utils/UI/TextRenderer.h (52%) rename {Framework/Source/Utils => Source/Falcor/Utils/UI}/UserInput.h (97%) rename {Framework/Source => Source/Falcor}/Utils/Video/VideoEncoder.cpp (59%) rename {Framework/Source => Source/Falcor}/Utils/Video/VideoEncoder.h (83%) create mode 100644 Source/Falcor/Utils/Video/VideoEncoderUI.cpp rename {Framework/Source => Source/Falcor}/Utils/Video/VideoEncoderUI.h (73%) create mode 100644 Source/Falcor/dependencies.xml create mode 100644 Source/Falcor/stdafx.cpp create mode 100644 Source/Falcor/stdafx.h create mode 100644 Source/Mogwai/Data/BSDFViewer.py create mode 100644 Source/Mogwai/Data/Config.py rename Samples/RenderGraph/RenderGraphViewer/Data/forward_renderer.py => Source/Mogwai/Data/ForwardRenderer.py (57%) create mode 100644 Source/Mogwai/Data/PathTracer.py create mode 100644 Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp create mode 100644 Source/Mogwai/Extensions/Capture/CaptureTrigger.h create mode 100644 Source/Mogwai/Extensions/Capture/FrameCapture.cpp create mode 100644 Source/Mogwai/Extensions/Capture/FrameCapture.h create mode 100644 Source/Mogwai/Extensions/Capture/VideoCapture.cpp create mode 100644 Source/Mogwai/Extensions/Capture/VideoCapture.h create mode 100644 Source/Mogwai/Mogwai.cpp create mode 100644 Source/Mogwai/Mogwai.h rename Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj => Source/Mogwai/Mogwai.vcxproj (66%) create mode 100644 Source/Mogwai/Mogwai.vcxproj.filters create mode 100644 Source/Mogwai/MogwaiScripting.cpp create mode 100644 Source/Mogwai/MogwaiSettings.cpp rename Framework/Source/API/ResourceViews.cpp => Source/Mogwai/MogwaiSettings.h (59%) create mode 100644 Source/Mogwai/Testing/testCSM.py create mode 100644 Source/Mogwai/Testing/testFXAA.py create mode 100644 Source/Mogwai/Testing/testForwardRendering.py create mode 100644 Source/Mogwai/Testing/testGaussianBlur.py create mode 100644 Source/Mogwai/Testing/testSSAO.py create mode 100644 Source/Mogwai/Testing/testSVGF.py create mode 100644 Source/Mogwai/Testing/testTAA.py create mode 100644 Source/Mogwai/Testing/testToneMapping.py create mode 100644 Source/Mogwai/stdafx.cpp create mode 100644 Source/Mogwai/stdafx.h create mode 100644 Source/RenderPasses/AccumulatePass/Accumulate.cs.slang create mode 100644 Source/RenderPasses/AccumulatePass/AccumulatePass.cpp create mode 100644 Source/RenderPasses/AccumulatePass/AccumulatePass.h rename Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj => Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj (69%) rename Samples/Core/MultiPassPostProcess/MultiPassPostProcess.filters => Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj.filters (55%) create mode 100644 Source/RenderPasses/AccumulatePass/x64/Debug/AccumulatePass.log create mode 100644 Source/RenderPasses/BSDFViewer/BSDFViewer.cpp create mode 100644 Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang create mode 100644 Source/RenderPasses/BSDFViewer/BSDFViewer.h create mode 100644 Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj create mode 100644 Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj.filters create mode 100644 Source/RenderPasses/BSDFViewer/BSDFViewerParams.h create mode 100644 Source/RenderPasses/DebugPasses/ComparisonPass.cpp create mode 100644 Source/RenderPasses/DebugPasses/ComparisonPass.h create mode 100644 Source/RenderPasses/DebugPasses/Data/Comparison.ps.slang rename Samples/Effects/AmbientOcclusion/Data/ApplyAO.ps.hlsl => Source/RenderPasses/DebugPasses/Data/InvalidPixelDetection.ps.slang (72%) rename Samples/Raytracing/PathTracer/Data/Accumulate.slang => Source/RenderPasses/DebugPasses/Data/SideBySide.ps.slang (76%) rename Samples/Core/ShaderBuffers/Data/ShaderBuffers.cs.hlsl => Source/RenderPasses/DebugPasses/Data/SplitScreen.ps.slang (77%) rename Framework/Source/Graphics/Paths/MovableObject.h => Source/RenderPasses/DebugPasses/DebugPasses.cpp (67%) rename Samples/ForwardRenderer/ForwardRenderer.vcxproj => Source/RenderPasses/DebugPasses/DebugPasses.vcxproj (64%) create mode 100644 Source/RenderPasses/DebugPasses/DebugPasses.vcxproj.filters create mode 100644 Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp rename Samples/Core/ComputeShader/ComputeShader.h => Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h (66%) create mode 100644 Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp rename Samples/Core/ProjectTemplate/ProjectTemplate.h => Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h (69%) create mode 100644 Source/RenderPasses/DebugPasses/SideBySidePass/Testing/testSideBySide.py rename Samples/Effects/Shadows/Data/VisualizeMap.slang => Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp (72%) rename Framework/Source/Utils/ThreadPool.h => Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h (75%) create mode 100644 Source/RenderPasses/DebugPasses/SplitScreenPass/Testing/testSplitScreen.py create mode 100644 Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp create mode 100644 Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h rename Samples/Core/StereoRendering/StereoRendering.vcxproj => Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.vcxproj (69%) create mode 100644 Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang create mode 100644 Source/RenderPasses/GBuffer/GBuffer.cpp create mode 100644 Source/RenderPasses/GBuffer/GBuffer.h create mode 100644 Source/RenderPasses/GBuffer/GBuffer.vcxproj create mode 100644 Source/RenderPasses/GBuffer/GBuffer.vcxproj.filters rename Samples/Effects/HDRToneMapping/Data/HDRToneMapping.hlsl => Source/RenderPasses/GBuffer/GBufferHelpers.slang (55%) rename Framework/Source/Graphics/Model/ModelRenderer.h => Source/RenderPasses/GBuffer/GBufferParams.h (69%) create mode 100644 Source/RenderPasses/GBuffer/GBufferRT.cpp create mode 100644 Source/RenderPasses/GBuffer/GBufferRT.h create mode 100644 Source/RenderPasses/GBuffer/GBufferRaster.cpp rename {Samples/Raytracing/PathTracer/RenderPasses => Source/RenderPasses/GBuffer}/GBufferRaster.h (67%) create mode 100644 Source/RenderPasses/GBuffer/RasterPrimary.slang create mode 100644 Source/RenderPasses/GBuffer/RaytracePrimary.rt.slang create mode 100644 Source/RenderPasses/GBuffer/Testing/test_rasterGbuffer.py create mode 100644 Source/RenderPasses/GBuffer/Testing/test_rayGBuffer.py create mode 100644 Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp create mode 100644 Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h create mode 100644 Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang rename Samples/Core/ComputeShader/ComputeShader.vcxproj => Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj (69%) rename Samples/Effects/Shadows/Shadows.filters => Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj.filters (51%) create mode 100644 Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.cpp create mode 100644 Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.h create mode 100644 Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj rename Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj.filters => Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj.filters (66%) create mode 100644 Source/RenderPasses/PathTracer/LoadGBuffer.slang create mode 100644 Source/RenderPasses/PathTracer/Logging.cpp create mode 100644 Source/RenderPasses/PathTracer/Logging.h create mode 100644 Source/RenderPasses/PathTracer/Logging.slang create mode 100644 Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.cpp rename Samples/Effects/HashedAlpha/HashedAlpha.h => Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.h (53%) create mode 100644 Source/RenderPasses/PathTracer/Megakernel/PathTracer.rt.slang create mode 100644 Source/RenderPasses/PathTracer/Megakernel/PathTracer.slang create mode 100644 Source/RenderPasses/PathTracer/Megakernel/RayData.slang create mode 100644 Source/RenderPasses/PathTracer/PathData.slang create mode 100644 Source/RenderPasses/PathTracer/PathTracer.cpp create mode 100644 Source/RenderPasses/PathTracer/PathTracer.h rename {Samples/Raytracing => Source/RenderPasses}/PathTracer/PathTracer.vcxproj (63%) create mode 100644 Source/RenderPasses/PathTracer/PathTracer.vcxproj.filters create mode 100644 Source/RenderPasses/PathTracer/PathTracerHelpers.slang create mode 100644 Source/RenderPasses/PathTracer/PathTracerParams.h create mode 100644 Source/RenderPasses/PathTracer/StaticParams.slang create mode 100644 Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang create mode 100644 Source/RenderPasses/PixelInspectorPass/PixelInspectorData.h create mode 100644 Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp create mode 100644 Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h create mode 100644 Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj create mode 100644 Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj.filters create mode 100644 Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang create mode 100644 Source/RenderPasses/SVGFPass/SVGFCommon.slang.h create mode 100644 Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang rename Samples/Effects/AmbientOcclusion/Data/AOPrePass.ps.hlsl => Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang (80%) rename Samples/Core/SimpleDeferred/Data/DeferredPass.ps.hlsl => Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang (77%) create mode 100644 Source/RenderPasses/SVGFPass/SVGFPass.cpp create mode 100644 Source/RenderPasses/SVGFPass/SVGFPass.h rename Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj => Source/RenderPasses/SVGFPass/SVGFPass.vcxproj (65%) create mode 100644 Source/RenderPasses/SVGFPass/SVGFPass.vcxproj.filters create mode 100644 Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang create mode 100644 Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.cpp rename Samples/Raytracing/PathTracer/PathTracer.h => Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.h (64%) rename Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj => Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj (85%) rename Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj.filters => Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj.filters (61%) create mode 100644 Source/RenderPasses/TemporalDelayPass/Testing/testTemporalDelayPass.py create mode 100644 Source/RenderPasses/make_new_pass_project.py rename Samples/Core/StereoRendering/Data/StereoRendering.gs.hlsl => Source/Samples/CudaInterop/CopySurface.cu (60%) create mode 100644 Source/Samples/CudaInterop/CopySurface.h create mode 100644 Source/Samples/CudaInterop/CudaInterop.cpp create mode 100644 Source/Samples/CudaInterop/CudaInterop.h rename Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj => Source/Samples/CudaInterop/CudaInterop.vcxproj (60%) create mode 100644 Source/Samples/CudaInterop/FalcorCUDA.cpp rename Framework/Source/Graphics/TextureHelper.h => Source/Samples/CudaInterop/FalcorCUDA.h (52%) create mode 100644 Source/Samples/CudaInterop/FalcorCUDA.props create mode 100644 Source/Samples/CudaInterop/README.md rename {Samples/Raytracing => Source/Samples}/HelloDXR/Data/HelloDXR.ps.hlsl (83%) rename {Samples/Raytracing => Source/Samples}/HelloDXR/Data/HelloDXR.rt.hlsl (75%) rename {Samples/Raytracing => Source/Samples}/HelloDXR/HelloDXR.cpp (62%) rename {Samples/Raytracing => Source/Samples}/HelloDXR/HelloDXR.h (68%) rename {Samples/Raytracing => Source/Samples}/HelloDXR/HelloDXR.vcxproj (92%) rename {Samples/Raytracing => Source/Samples}/HelloDXR/HelloDXR.vcxproj.filters (100%) rename {Samples/Utils => Source/Samples}/ModelViewer/Data/ModelViewer.ps.hlsl (78%) create mode 100644 Source/Samples/ModelViewer/ModelViewer.cpp rename {Samples/Utils => Source/Samples}/ModelViewer/ModelViewer.h (60%) rename {Samples/Utils => Source/Samples}/ModelViewer/ModelViewer.vcxproj (90%) rename {Samples/Utils => Source/Samples}/ModelViewer/ModelViewer.vcxproj.filters (100%) rename {Samples/Core => Source/Samples}/ProjectTemplate/ProjectTemplate.cpp (73%) create mode 100644 Source/Samples/ProjectTemplate/ProjectTemplate.h rename {Samples/Core => Source/Samples}/ProjectTemplate/ProjectTemplate.vcxproj (89%) rename {Samples/Core => Source/Samples}/ProjectTemplate/ProjectTemplate.vcxproj.filters (100%) rename {Samples/Core => Source/Samples}/ShaderToy/Data/toy.hlsl (98%) rename {Samples/Core => Source/Samples}/ShaderToy/Data/toyContainer.hlsl (97%) rename {Samples/Core => Source/Samples}/ShaderToy/ShaderToy.cpp (72%) rename {Samples/Core => Source/Samples}/ShaderToy/ShaderToy.h (71%) rename {Samples/Core => Source/Samples}/ShaderToy/ShaderToy.vcxproj (90%) rename {Samples/Core => Source/Samples}/ShaderToy/ShaderToy.vcxproj.filters (100%) rename {Samples/Core => Source/Samples}/make_new_project.py (91%) rename {Samples/Utils => Source/Tools}/FalcorTest/FalcorTest.cpp (83%) rename {Samples/Utils => Source/Tools}/FalcorTest/FalcorTest.h (88%) rename {Samples/Utils => Source/Tools}/FalcorTest/FalcorTest.vcxproj (59%) create mode 100644 Source/Tools/FalcorTest/FalcorTest.vcxproj.filters create mode 100644 Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp rename Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.cpp => Source/Tools/FalcorTest/Tests/Core/BufferTests.cs.slang (58%) create mode 100644 Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp rename Samples/Raytracing/PathTracer/Data/GGXGIIndirectRay.slang => Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cs.slang (50%) create mode 100644 Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cs.slang create mode 100644 Source/Tools/FalcorTest/Tests/Scene/EnvProbeTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cpp rename Framework/Source/Data/Framework/Shaders/Blit.vs.slang => Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cs.slang (80%) create mode 100644 Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cpp rename {Samples/Utils/FalcorTest/Data => Source/Tools/FalcorTest/Tests/ShadingUtils}/ShadingUtilsTests.cs.slang (82%) create mode 100644 Source/Tools/FalcorTest/Tests/Slang/SlangShared.h create mode 100644 Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Slang/SlangTests.cs.slang create mode 100644 Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/AABBTests.cs.slang create mode 100644 Source/Tools/FalcorTest/Tests/Utils/AlignedAllocatorTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp rename Framework/Source/FalcorExperimental.h => Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cs.slang (54%) create mode 100644 Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp rename Samples/Core/MultiPassPostProcess/MultiPassPostProcess.h => Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cs.slang (52%) create mode 100644 Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cs.slang create mode 100644 Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp rename Samples/Raytracing/PathTracer/Data/GGXGIShadowRay.slang => Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cs.slang (59%) create mode 100644 Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp create mode 100644 Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp rename {Samples/RenderGraph/RenderGraphViewer => Source/Tools/RenderGraphEditor}/Data/DefaultPassIcon.png (100%) rename {Samples/RenderGraph => Source/Tools}/RenderGraphEditor/RenderGraphEditor.cpp (55%) rename {Samples/RenderGraph => Source/Tools}/RenderGraphEditor/RenderGraphEditor.h (82%) rename Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj => Source/Tools/RenderGraphEditor/RenderGraphEditor.vcxproj (65%) create mode 100644 Tests/GraphTests.py delete mode 100644 Tests/InternalConfig.py rename Tests/{ => Old}/CloneRepo.py (100%) rename Tests/{ => Old}/CollectAndEmailResults.py (100%) rename Tests/{ => Old}/GenerateReferences.py (86%) rename Tests/{ => Old}/GetBuildStatus.py (90%) rename Tests/{ => Old}/RemoveDirectoryTree.bat (100%) create mode 100644 Tests/Old/RunAllTests.py rename Tests/{ => Old}/RunPassTests.py (63%) rename Tests/{ => Old}/StartBuildTest.py (75%) create mode 100644 Tests/Old/TeamCityCommon.py rename Tests/{ => Old}/WriteTestResultsToHTML.py (98%) delete mode 100644 Tests/ReadMe.txt delete mode 100644 Tests/TeamCityCommon.py create mode 100644 Tests/TestConfig.py create mode 100644 Tests/TestFalcor.py delete mode 100644 Tests/build.xml rename {Framework/Documentation => Tools}/Doxyfile (100%) rename {Framework/SceneScripts => Tools}/buildTiledScene.py (100%) delete mode 100644 dependencies.xml delete mode 100644 update_dependencies.bat diff --git a/.editorconfig b/.editorconfig index 307358eec..c2fb9112a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,3 +17,8 @@ charset = utf-8 # Override trailing whitespace setting for Markdown since there it's actually useful [*.{md}] trim_trailing_whitespace = false + +# Override settings for project files to use the same settings as Visual Studio +[*.{vcxproj,vcxproj.filters}] +indent_size = 2 +insert_final_newline = false diff --git a/.gitignore b/.gitignore index dca1e5570..abd6cc960 100644 --- a/.gitignore +++ b/.gitignore @@ -9,28 +9,16 @@ Bin/* *.suo *.ptx *.pyc -Falcor.VC.db -FalcorTest.VC.db -Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.VC.db +*.VC.db FalcorTest/TestResults/* -Falcor.VC.VC.opendb +*.VC.opendb .vs/* .vscode/ *.pyc *.o *slang-dump-* - -# A bit of a song and dance to ignore all the files -# in the Slang codebase except for the ones we need. -Framework/Externals/slang/* -!Framework/Externals/slang/source/ -Framework/Externals/slang/source/**/*.vcxproj -Framework/Externals/slang/source/**/*.filters -Framework/Externals/slang/source/**/*.natvis -!Framework/Externals/slang/source/**/*.cpp -!Framework/Externals/slang/source/**/*.cpp -!Framework/Externals/slang/source/**/*.h -Framework/Externals/* -!Framework/Externals/dear_imgui_addons/ -!Framework/Externals/dear_imgui_addons/imguinodegrapheditor/ +Source/Externals/.packman/* Media +/Tests/TestResults/* +/Tests/Solution_BuildLog.txt +/Tests/robocopy.txt diff --git a/Build/deploycommon.bat b/Build/deploycommon.bat new file mode 100644 index 000000000..a21e3d08b --- /dev/null +++ b/Build/deploycommon.bat @@ -0,0 +1,48 @@ +@echo off +setlocal + +rem %1 -> Falcor Core Directory Path +rem %2 -> Platform Short Name +rem %3 -> Output Directory +rem %4 -> WINDSDK Directory + +setlocal + +SET ExtDir=%1\Externals\.packman\ +SET OutDir=%3 +SET FalcorDir=%1\Falcor\ +if not exist "%OutDir%" mkdir "%OutDir%" + +rem Copy Falcor's files +IF not exist %OutDir%\Data\ mkdir %OutDir%\Data >nul +IF exist %FalcorDir%\ShadingUtils\ (xcopy %FalcorDir%\ShadingUtils\*.* %OutDir%\Data /s /y /d) +IF exist %FalcorDir%\Raytracing\Data\ (xcopy %FalcorDir%\Raytracing\Data\*.* %OutDir%\Data /s /y /d /q >nul) +call %~dp0\deployproject.bat %FalcorDir% %OutDir% + +rem Copy externals +robocopy %ExtDir%\Python\ %OutDir% Python37*.dll /r:0 >nul +robocopy %ExtDir%\Python %OutDir%\Python /E /r:0 >nul +robocopy %ExtDir%\AntTweakBar\lib %OutDir% AntTweakBar64.dll /r:0 >nul +robocopy %ExtDir%\FreeImage %OutDir% freeimage.dll /r:0 >nul +robocopy %ExtDir%\assimp\bin\%2 %OutDir% *.dll /r:0 >nul +robocopy %ExtDir%\FFMpeg\bin\%2 %OutDir% *.dll /r:0 >nul +robocopy %ExtDir%\dxcompiler\%2 %OutDir% dxcompiler.dll /r:0 >nul +rem robocopy %ExtDir%\dxcompiler\windows-x86_64\release\%2 %OutDir% *.dll /r:0 >nul +robocopy %ExtDir%\OptiX\bin64 %OutDir% *.dll /r:0 >nul +robocopy %ExtDir%\openvr\bin\win64 %OutDir% openvr_api.dll /r:0 >nul +robocopy %ExtDir%\Slang\bin\windows-x64\release %OutDir% *.dll /r:0 >nul +robocopy %ExtDir%\GLFW\lib %OutDir% *.dll /r:0 >nul +robocopy %ExtDir%\WinPixEventRuntime\bin\x64 %OutDir% WinPixEventRuntime.dll /r:0 >nul +robocopy "%~4\Redist\D3D\%2" %OutDir% dxil.dll /r:0 >nul + +rem Copy NVAPI +set NvApiDir=%ExtDir%\NVAPI +IF exist %NvApiDir% ( + IF not exist %OutDir%\Data\NVAPI mkdir %OutDir%\Data\NVAPI >nul + copy /y %NvApiDir%\nvHLSLExtns.h %OutDir%\Data\NVAPI + copy /y %NvApiDir%\nvHLSLExtnsInternal.h %OutDir%\Data\NVAPI + copy /y %NvApiDir%\nvShaderExtnEnums.h %OutDir%\Data\NVAPI +) + +rem robocopy sets the error level to something that is not zero even if the copy operation was successful. Set the error level to zero +exit /b 0 diff --git a/Build/deployproject.bat b/Build/deployproject.bat new file mode 100644 index 000000000..7b158b8dd --- /dev/null +++ b/Build/deployproject.bat @@ -0,0 +1,7 @@ + +rem %1==projectDir %2==outputdir +setlocal + +IF not exist %2\Data\ ( mkdir %2\Data >nul ) +IF exist %1\data\ ( xcopy %1\Data\*.* %2\Data /s /y /d /q >nul) + diff --git a/packman/config.packman.xml b/Build/packman/config.packman.xml similarity index 100% rename from packman/config.packman.xml rename to Build/packman/config.packman.xml diff --git a/packman/packman b/Build/packman/packman old mode 100755 new mode 100644 similarity index 100% rename from packman/packman rename to Build/packman/packman diff --git a/packman/packman.cmd b/Build/packman/packman.cmd similarity index 100% rename from packman/packman.cmd rename to Build/packman/packman.cmd diff --git a/packman/win-bootstrap/configure.bat b/Build/packman/win-bootstrap/configure.bat similarity index 100% rename from packman/win-bootstrap/configure.bat rename to Build/packman/win-bootstrap/configure.bat diff --git a/packman/win-bootstrap/fetch_file_from_s3.cmd b/Build/packman/win-bootstrap/fetch_file_from_s3.cmd similarity index 100% rename from packman/win-bootstrap/fetch_file_from_s3.cmd rename to Build/packman/win-bootstrap/fetch_file_from_s3.cmd diff --git a/packman/win-bootstrap/fetch_file_from_s3.ps1 b/Build/packman/win-bootstrap/fetch_file_from_s3.ps1 similarity index 100% rename from packman/win-bootstrap/fetch_file_from_s3.ps1 rename to Build/packman/win-bootstrap/fetch_file_from_s3.ps1 diff --git a/packman/win-bootstrap/fetch_file_from_url.ps1 b/Build/packman/win-bootstrap/fetch_file_from_url.ps1 similarity index 100% rename from packman/win-bootstrap/fetch_file_from_url.ps1 rename to Build/packman/win-bootstrap/fetch_file_from_url.ps1 diff --git a/packman/win-bootstrap/generate_temp_file_name.ps1 b/Build/packman/win-bootstrap/generate_temp_file_name.ps1 similarity index 100% rename from packman/win-bootstrap/generate_temp_file_name.ps1 rename to Build/packman/win-bootstrap/generate_temp_file_name.ps1 diff --git a/packman/win-bootstrap/generate_temp_folder.ps1 b/Build/packman/win-bootstrap/generate_temp_folder.ps1 similarity index 100% rename from packman/win-bootstrap/generate_temp_folder.ps1 rename to Build/packman/win-bootstrap/generate_temp_folder.ps1 diff --git a/packman/win-bootstrap/install_package.py b/Build/packman/win-bootstrap/install_package.py similarity index 100% rename from packman/win-bootstrap/install_package.py rename to Build/packman/win-bootstrap/install_package.py diff --git a/Build/patchpropssheet.py b/Build/patchpropssheet.py new file mode 100644 index 000000000..eaf2117f6 --- /dev/null +++ b/Build/patchpropssheet.py @@ -0,0 +1,41 @@ +import sys +import os + +def patchGroup(propSheet, group, val): + groupStart = "<" + group + ">" + groupEnd = "" + s = propSheet.find(groupStart) + e = propSheet.find(groupEnd) + if(s == -1 or e == -1): + sys.exit("Can't find a `" + groupStart + "` section in the file. This is probably because someone deleted it for the property sheet. Revert the changes and try again.\n") + + if(s >= e): + sys.exit("The property sheet is corrupted. `" + groupEnd + "` can't appear before `" + groupStart + "` \n") + + s += len(groupStart) + propSheet = propSheet[:s] + val + propSheet[e:] + return propSheet + +if(len(sys.argv) != 4): + sys.exit("Usage:\npatchpropssheet.py ") + +coreDir = sys.argv[1] +solutionDir = sys.argv[2] +backend = sys.argv[3] +propsFileName = coreDir + "\\Falcor\\falcor.props" +# Open and read the file +f = open(propsFileName) +propSheet = f.read() +f.close() + +# Get a relative path from the Current Solution Directory to the Falcor Core Directory. +relcorepath = os.path.relpath(coreDir, solutionDir) +coreDir = "$(SolutionDir)\\" + relcorepath + +propSheet = patchGroup(propSheet, "FALCOR_CORE_DIRECTORY", coreDir) +propSheet = patchGroup(propSheet, "FALCOR_BACKEND", backend) + +# Save the file +f = open(propsFileName, "w") +f.write(propSheet) +f.close() \ No newline at end of file diff --git a/Framework/BuildScripts/prebuild.bat b/Build/prebuild.bat similarity index 73% rename from Framework/BuildScripts/prebuild.bat rename to Build/prebuild.bat index aab881e13..b4c60c8a9 100644 --- a/Framework/BuildScripts/prebuild.bat +++ b/Build/prebuild.bat @@ -41,14 +41,11 @@ if /I "%6"=="ReleaseD3D12" set falcor_backend=FALCOR_D3D12 if /I "%6"=="DebugVK" set falcor_backend=FALCOR_VK if /I "%6"=="ReleaseVK" set falcor_backend=FALCOR_VK -if /I "%6"=="DebugDXR" set falcor_backend=FALCOR_DXR -if /I "%6"=="ReleaseDXR" set falcor_backend=FALCOR_DXR - -rem Change the Props File to Align with the Backend. -rem This also changes the FALCOR_CORE_DIRECTORY -start /wait /b %1BuildScripts\PatchFalcorProps\PatchFalcorPropertySheet.exe %1 %2 %falcor_backend% - rem Call Update Dependencies - Runs packman. -call %1\..\update_dependencies.bat +call %~dp0\update_dependencies.bat %1\Falcor\dependencies.xml if errorlevel 1 exit /b 1 +%1\Externals\.packman\Python\python.exe %~dp0\patchpropssheet.py %1 %2 %falcor_backend% +if errorlevel 1 exit /b 1 +if exist %1\Internal\internal_dependencies.xml (call %~dp0\update_dependencies.bat %1\Internal\internal_dependencies.xml) +if errorlevel 1 exit /b 1 diff --git a/Build/update_dependencies.bat b/Build/update_dependencies.bat new file mode 100644 index 000000000..a26157ca7 --- /dev/null +++ b/Build/update_dependencies.bat @@ -0,0 +1,10 @@ +@echo off +IF [%1] == [] GOTO helpMsg +set PM_DISABLE_VS_WARNING=true +call "%~dp0packman\packman.cmd " pull "%1" --platform win +if errorlevel 1 exit /b 1 +exit /b 0 + +:helpMsg +echo Please specify a dependency file +exit /b 1 \ No newline at end of file diff --git a/update_dependencies.sh b/Build/update_dependencies.sh old mode 100755 new mode 100644 similarity index 100% rename from update_dependencies.sh rename to Build/update_dependencies.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 210338cda..65b57bb9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,33 +1,14 @@ -v3.2.2 ------- -Bug Fixes: -- Fixed referencing a temporary variable in Vulkan MSAA state creation that causes it to not work on some systems -- Fixed unreachable code when reflecting sample shading configuration -- Fixed comment in Material.h incorrectly describing material channel layout - -Dependencies: -- Updates Slang to 0.12.5 - -v3.2.1 ------- +WIP +---- +- Added `Fbo::create()` which creates a new objects given a list of textures +- Added `RenderContext::clearTexture()` which uses the bind-flag to determin which clear function to call +- Added `Resource::BindFlags::AllColorViews` and `Resource::BindFlags::AllDepthViews` - `Ctrl+Pause` freezes/unfreezes the renderer. This if useful in low framerates situations when the user wants to change an attribute using the GUI -- File open dialog filters for images now include .hdr files for HDR formats (Bitmap::getFileDialogFilters) -- Added ability to force execution of render passes through a flag when adding the pass to a render graph -- GUI groups can be opened as a separate window by right-clicking on the group header bar -- Added support for sliders in the GUI -- Added support for buttons with images in the GUI (Gui::addImageButton) -- Added option to include a close button when creating GUI windows - -New Samples: -- PathTracer: A basic path tracer implemented using DXR and the render graph system - -Bug Fixes: -- Messages logged in dll's will no longer output to a separate text file -- Updated make_new_project.py to use Python 3 print()'s -- Python copied from Externals to executable folder after build to be used as Python home directory - -Dependencies: -- Updated Slang to 0.11.21 +- Added `ComputeContext::dispatchProgram()` - a convenience function to dispatch a compute program without altering the state of context. +- Integration with WinPixEvent +- Refactored `ProgramVars::operator[]` to also work with resources +- New build rule for hlsl and slang files, which will copy them to the `Data` directory while preserving the directory structure +- Packman fetches into Externals/.packman v3.2 ------ @@ -54,7 +35,7 @@ v3.2 - Renamed getGeometricNormal to getGeometricNormalW to clarify it's in world space - Loading of GLTF models has been enabled -New Samples: +New samples - RenderGraphEditor: A visual, node-based tool for creating and editing render graph scripts. - RenderGraphViewer: Load scenes and render them with a render graph. - SamplePassLibrary: Demonstration of how to write render passes that compile to DLLs, and how they can be loaded from render graph scripts. @@ -70,7 +51,7 @@ Deprecations: Dependencies: - Updated packman to 5.7.1 -- Updated Slang to 0.11.8 +- Updated Slang to 0.11.7 - Updated Falcor Media to 2.2.1 v3.1 diff --git a/Framework/Documentation/CodeConvention.cpp b/CodingConvention.cpp similarity index 84% rename from Framework/Documentation/CodeConvention.cpp rename to CodingConvention.cpp index f0fc4f16c..9f6458225 100644 --- a/Framework/Documentation/CodeConvention.cpp +++ b/CodingConvention.cpp @@ -24,6 +24,14 @@ Function names should be descriptive * Function that perform an action should be named after the action it performs - Fbo::clear(), createTextureFromFile(). * Getters/Setters should start with 'get' and 'set' * Functions names that return a bool should be phrased as a question - isWhite(), doesFileExist(), hasTexture() + +Use the following pointers guidelines: +- Every class you add must define a SharedPtr, like so - `using SharedPtr = std::shared_ptr` +- Classes must expose a `SharedPtr create(...);` function. All the class' constructors must be private. +- When writing interfaces: + o Use a raw-pointer/reference if the object is used only during the function's lifetime (i.e. the function doesn't store it in a member variable for future use) + o Use `const SomeObject::SharedPtr&` if the called function takes ownership of the object (i.e. store it in a member variable for future use). The const-ref thing is to avoid incrementing the reference twice (last I checked, the compiler didn't optimize it). +We don't really use unique_ptr. shared_ptr works just fine. Not to say that you can't use unique_ptr, but if you do - you better have a very good reason to do that. */ ///////////////////////////////// Variable prefixes //////////////////////////////////// @@ -39,7 +47,7 @@ In addition 'p' is used for pointers. //Global Variables: const uint32_t kConstGlobal; // compile-time-const, so 'k' takes precedence int32_t gSomeGlobal; // global start with 'g' -static int gStaticGlobal; // Static globals start with 'g' +static int32_t gStaticGlobal; // Static globals start with 'g' void* gpSomePointer; // Global variables which is a pointer is prefixed with 'gp' const void* gpPointer2; // Not compile-time constant. diff --git a/Falcor.sln b/Falcor.sln index 1f3eb702a..a7534d376 100644 --- a/Falcor.sln +++ b/Falcor.sln @@ -1,76 +1,62 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27428.2027 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Falcor", "Framework\Source\Falcor.vcxproj", "{3B602F0E-3834-4F73-B97D-7DFC91597A98}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{518F9E6D-D9DE-4557-94EC-F0F466354504}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{CA90E299-AACA-4629-AA2C-E5DA38FFB78D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MultiPassPostProcess", "Samples\Core\MultiPassPostProcess\MultiPassPostProcess.vcxproj", "{282AAB9B-2150-447C-9C27-62C38C23761E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectTemplate", "Samples\Core\ProjectTemplate\ProjectTemplate.vcxproj", "{605856E4-34D4-40DF-B859-EEA3A7D52A7B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderBuffers", "Samples\Core\ShaderBuffers\ShaderBuffers.vcxproj", "{E9189681-F552-4811-9B9C-C88E63D21363}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderToy", "Samples\Core\ShaderToy\ShaderToy.vcxproj", "{8AB4CF3D-9824-4390-8569-B07776C4D1F6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleDeferred", "Samples\Core\SimpleDeferred\SimpleDeferred.vcxproj", "{6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StereoRendering", "Samples\Core\StereoRendering\StereoRendering.vcxproj", "{7C6C43DE-EEF4-4165-BE92-ED753D3799EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{152F0E49-0B22-4359-B8FB-BD76093D36DE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ModelViewer", "Samples\Utils\ModelViewer\ModelViewer.vcxproj", "{7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{350A0B15-98C0-45E3-872B-4FEFB47AA37C}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SceneEditor", "Samples\Utils\SceneEditor\SceneEditor.vcxproj", "{DE6A0005-923E-4007-B58C-3C35F690773F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{4B8EAC4B-FFDF-4CCA-A6FE-4505631E51EC}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComputeShader", "Samples\Core\ComputeShader\ComputeShader.vcxproj", "{283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{935D7586-B55D-431A-A0ED-338383DE1A1E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LearningWithEmbeddedPython", "Samples\Core\LearningWithEmbeddedPython\LearningWithEmbeddedPython.vcxproj", "{B7D37434-A294-4E67-8420-AD09C54C10EF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FalcorTest", "Source\Tools\FalcorTest\FalcorTest.vcxproj", "{20401FAD-6022-8EB7-2F78-41369B8F0F49}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ForwardRenderer", "Samples\ForwardRenderer\ForwardRenderer.vcxproj", "{ADDD1F96-AE44-40BA-9942-0F056F96FA4B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderGraphEditor", "Source\Tools\RenderGraphEditor\RenderGraphEditor.vcxproj", "{DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Effects", "Effects", "{6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Falcor", "Source\Falcor\Falcor.vcxproj", "{2C535635-E4C5-4098-A928-574F0E7CD5F9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AmbientOcclusion", "Samples\Effects\AmbientOcclusion\AmbientOcclusion.vcxproj", "{E6F10A52-9C29-47B0-8BAE-46C146EB7163}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildScripts", "BuildScripts", "{0ABDD59E-937B-41FF-B78A-7F47CBCCA387}" + ProjectSection(SolutionItems) = preProject + Build\deploycommon.bat = Build\deploycommon.bat + Build\deployproject.bat = Build\deployproject.bat + Build\patchpropssheet.py = Build\patchpropssheet.py + Build\prebuild.bat = Build\prebuild.bat + Build\update_dependencies.bat = Build\update_dependencies.bat + EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HashedAlpha", "Samples\Effects\HashedAlpha\HashedAlpha.vcxproj", "{1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mogwai", "Source\Mogwai\Mogwai.vcxproj", "{204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HDRToneMapping", "Samples\Effects\HDRToneMapping\HDRToneMapping.vcxproj", "{0A6AC638-6567-49F9-B328-66BA201C74B6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RenderPasses", "RenderPasses", "{D16038A7-B031-4181-B4A1-2C416C02330C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Particles", "Samples\Effects\Particles\Particles.vcxproj", "{4BD89013-BE22-47CC-BCEC-8A406C8061A0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TemporalDelayPass", "Source\RenderPasses\TemporalDelayPass\TemporalDelayPass.vcxproj", "{6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shadows", "Samples\Effects\Shadows\Shadows.vcxproj", "{613640EA-CBBD-4B9D-931C-00110D5C4007}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DebugPasses", "Source\RenderPasses\DebugPasses\DebugPasses.vcxproj", "{CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SkyBoxRenderer", "Samples\Effects\SkyBoxRenderer\SkyBoxRenderer.vcxproj", "{0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloDXR", "Source\Samples\HelloDXR\HelloDXR.vcxproj", "{71B60B71-89A2-4196-BFB9-4A848CF6C541}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Raytracing", "Raytracing", "{6D4D8D4B-CFFB-455A-BFFC-9490C5583150}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ModelViewer", "Source\Samples\ModelViewer\ModelViewer.vcxproj", "{7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloDXR", "Samples\Raytracing\HelloDXR\HelloDXR.vcxproj", "{71B60B71-89A2-4196-BFB9-4A848CF6C541}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectTemplate", "Source\Samples\ProjectTemplate\ProjectTemplate.vcxproj", "{605856E4-34D4-40DF-B859-EEA3A7D52A7B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LightProbeViewer", "Samples\Utils\LightProbeViewer\LightProbeViewer.vcxproj", "{9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderToy", "Source\Samples\ShaderToy\ShaderToy.vcxproj", "{8AB4CF3D-9824-4390-8569-B07776C4D1F6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FalcorSharedObjects", "Framework\FalcorSharedObjects\FalcorSharedObjects.vcxproj", "{2C535635-E4C5-4098-A928-574F0E7CD5F9}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AccumulatePass", "Source\RenderPasses\AccumulatePass\AccumulatePass.vcxproj", "{081FD8DE-6C92-4CDC-84AD-C514F7E83F93}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RenderGraph", "RenderGraph", "{AC911782-AE2C-4026-9E0E-EB359ECDE830}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BSDFViewer", "Source\RenderPasses\BSDFViewer\BSDFViewer.vcxproj", "{B219C161-94D2-4DCB-A75D-E6A0906F534D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderGraphEditor", "Samples\RenderGraph\RenderGraphEditor\RenderGraphEditor.vcxproj", "{DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ErrorMeasurePass", "Source\RenderPasses\ErrorMeasurePass\ErrorMeasurePass.vcxproj", "{DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderGraphViewer", "Samples\RenderGraph\RenderGraphViewer\RenderGraphViewer.vcxproj", "{204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GBuffer", "Source\RenderPasses\GBuffer\GBuffer.vcxproj", "{CA155B01-7528-4D81-B5CD-E801B4AA4027}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SamplePassLibrary", "Samples\RenderGraph\SamplePassLibrary\SamplePassLibrary.vcxproj", "{232CBBB4-33D9-4445-BB62-08A7E7700147}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinimalPathTracer", "Source\RenderPasses\MinimalPathTracer\MinimalPathTracer.vcxproj", "{FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FalcorTest", "Samples\Utils\FalcorTest\FalcorTest.vcxproj", "{20401FAD-6022-8EB7-2F78-41369B8F0F49}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PathTracer", "Source\RenderPasses\PathTracer\PathTracer.vcxproj", "{99167900-C302-49E9-9C27-E6B3F666DB05}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{350A0B15-98C0-45E3-872B-4FEFB47AA37C}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PixelInspectorPass", "Source\RenderPasses\PixelInspectorPass\PixelInspectorPass.vcxproj", "{80DDDE59-A412-4E64-880A-6B52697C967B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PathTracer", "Samples\Raytracing\PathTracer\PathTracer.vcxproj", "{0B41644B-B687-44D8-9CD5-9D41E0011561}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SVGFPass", "Source\RenderPasses\SVGFPass\SVGFPass.vcxproj", "{691F64DD-0941-49DE-B52E-949341192154}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -80,164 +66,14 @@ Global ReleaseVK|x64 = ReleaseVK|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.DebugD3D12|x64.ActiveCfg = DebugD3D12|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.DebugD3D12|x64.Build.0 = DebugD3D12|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.DebugVK|x64.ActiveCfg = DebugVK|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.DebugVK|x64.Build.0 = DebugVK|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.ReleaseD3D12|x64.ActiveCfg = ReleaseD3D12|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.ReleaseD3D12|x64.Build.0 = ReleaseD3D12|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.ReleaseVK|x64.ActiveCfg = ReleaseVK|x64 - {3B602F0E-3834-4F73-B97D-7DFC91597A98}.ReleaseVK|x64.Build.0 = ReleaseVK|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.DebugD3D12|x64.Build.0 = Debug|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.DebugVK|x64.ActiveCfg = Debug|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.DebugVK|x64.Build.0 = Debug|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.ReleaseD3D12|x64.Build.0 = Release|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.ReleaseVK|x64.ActiveCfg = Release|x64 - {282AAB9B-2150-447C-9C27-62C38C23761E}.ReleaseVK|x64.Build.0 = Release|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugD3D12|x64.Build.0 = Debug|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugVK|x64.ActiveCfg = Debug|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugVK|x64.Build.0 = Debug|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseD3D12|x64.Build.0 = Release|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseVK|x64.ActiveCfg = Release|x64 - {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseVK|x64.Build.0 = Release|x64 - {E9189681-F552-4811-9B9C-C88E63D21363}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {E9189681-F552-4811-9B9C-C88E63D21363}.DebugD3D12|x64.Build.0 = Debug|x64 - {E9189681-F552-4811-9B9C-C88E63D21363}.DebugVK|x64.ActiveCfg = Debug|x64 - {E9189681-F552-4811-9B9C-C88E63D21363}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {E9189681-F552-4811-9B9C-C88E63D21363}.ReleaseD3D12|x64.Build.0 = Release|x64 - {E9189681-F552-4811-9B9C-C88E63D21363}.ReleaseVK|x64.ActiveCfg = Release|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugD3D12|x64.Build.0 = Debug|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugVK|x64.ActiveCfg = Debug|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugVK|x64.Build.0 = Debug|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseD3D12|x64.Build.0 = Release|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseVK|x64.ActiveCfg = Release|x64 - {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseVK|x64.Build.0 = Release|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.DebugD3D12|x64.Build.0 = Debug|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.DebugVK|x64.ActiveCfg = Debug|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.DebugVK|x64.Build.0 = Debug|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.ReleaseD3D12|x64.Build.0 = Release|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.ReleaseVK|x64.ActiveCfg = Release|x64 - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22}.ReleaseVK|x64.Build.0 = Release|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.DebugD3D12|x64.Build.0 = Debug|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.DebugVK|x64.ActiveCfg = Debug|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.DebugVK|x64.Build.0 = Debug|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.ReleaseD3D12|x64.Build.0 = Release|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.ReleaseVK|x64.ActiveCfg = Release|x64 - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE}.ReleaseVK|x64.Build.0 = Release|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugD3D12|x64.Build.0 = Debug|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugVK|x64.ActiveCfg = Debug|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugVK|x64.Build.0 = Debug|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseD3D12|x64.Build.0 = Release|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseVK|x64.ActiveCfg = Release|x64 - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseVK|x64.Build.0 = Release|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.DebugD3D12|x64.Build.0 = Debug|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.DebugVK|x64.ActiveCfg = Debug|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.DebugVK|x64.Build.0 = Debug|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.ReleaseD3D12|x64.Build.0 = Release|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.ReleaseVK|x64.ActiveCfg = Release|x64 - {DE6A0005-923E-4007-B58C-3C35F690773F}.ReleaseVK|x64.Build.0 = Release|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.DebugD3D12|x64.Build.0 = Debug|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.DebugVK|x64.ActiveCfg = Debug|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.DebugVK|x64.Build.0 = Debug|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.ReleaseD3D12|x64.Build.0 = Release|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.ReleaseVK|x64.ActiveCfg = Release|x64 - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18}.ReleaseVK|x64.Build.0 = Release|x64 - {B7D37434-A294-4E67-8420-AD09C54C10EF}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {B7D37434-A294-4E67-8420-AD09C54C10EF}.DebugVK|x64.ActiveCfg = Debug|x64 - {B7D37434-A294-4E67-8420-AD09C54C10EF}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {B7D37434-A294-4E67-8420-AD09C54C10EF}.ReleaseVK|x64.ActiveCfg = Release|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.DebugD3D12|x64.Build.0 = Debug|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.DebugVK|x64.ActiveCfg = Debug|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.DebugVK|x64.Build.0 = Debug|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.ReleaseD3D12|x64.Build.0 = Release|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.ReleaseVK|x64.ActiveCfg = Release|x64 - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B}.ReleaseVK|x64.Build.0 = Release|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.DebugD3D12|x64.Build.0 = Debug|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.DebugVK|x64.ActiveCfg = Debug|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.DebugVK|x64.Build.0 = Debug|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.ReleaseD3D12|x64.Build.0 = Release|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.ReleaseVK|x64.ActiveCfg = Release|x64 - {E6F10A52-9C29-47B0-8BAE-46C146EB7163}.ReleaseVK|x64.Build.0 = Release|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.DebugD3D12|x64.Build.0 = Debug|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.DebugVK|x64.ActiveCfg = Debug|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.DebugVK|x64.Build.0 = Debug|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.ReleaseD3D12|x64.Build.0 = Release|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.ReleaseVK|x64.ActiveCfg = Release|x64 - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988}.ReleaseVK|x64.Build.0 = Release|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.DebugD3D12|x64.Build.0 = Debug|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.DebugVK|x64.ActiveCfg = Debug|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.DebugVK|x64.Build.0 = Debug|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.ReleaseD3D12|x64.Build.0 = Release|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.ReleaseVK|x64.ActiveCfg = Release|x64 - {0A6AC638-6567-49F9-B328-66BA201C74B6}.ReleaseVK|x64.Build.0 = Release|x64 - {4BD89013-BE22-47CC-BCEC-8A406C8061A0}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {4BD89013-BE22-47CC-BCEC-8A406C8061A0}.DebugD3D12|x64.Build.0 = Debug|x64 - {4BD89013-BE22-47CC-BCEC-8A406C8061A0}.DebugVK|x64.ActiveCfg = Debug|x64 - {4BD89013-BE22-47CC-BCEC-8A406C8061A0}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {4BD89013-BE22-47CC-BCEC-8A406C8061A0}.ReleaseD3D12|x64.Build.0 = Release|x64 - {4BD89013-BE22-47CC-BCEC-8A406C8061A0}.ReleaseVK|x64.ActiveCfg = Release|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.DebugD3D12|x64.Build.0 = Debug|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.DebugVK|x64.ActiveCfg = Debug|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.DebugVK|x64.Build.0 = Debug|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.ReleaseD3D12|x64.Build.0 = Release|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.ReleaseVK|x64.ActiveCfg = Release|x64 - {613640EA-CBBD-4B9D-931C-00110D5C4007}.ReleaseVK|x64.Build.0 = Release|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.DebugD3D12|x64.Build.0 = Debug|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.DebugVK|x64.ActiveCfg = Debug|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.DebugVK|x64.Build.0 = Debug|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.ReleaseD3D12|x64.Build.0 = Release|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.ReleaseVK|x64.ActiveCfg = Release|x64 - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287}.ReleaseVK|x64.Build.0 = Release|x64 - {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugD3D12|x64.Build.0 = Debug|x64 - {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugVK|x64.ActiveCfg = Debug|x64 - {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseD3D12|x64.Build.0 = Release|x64 - {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseVK|x64.ActiveCfg = Release|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.DebugD3D12|x64.Build.0 = Debug|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.DebugVK|x64.ActiveCfg = Debug|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.DebugVK|x64.Build.0 = Debug|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.ReleaseD3D12|x64.Build.0 = Release|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.ReleaseVK|x64.ActiveCfg = Release|x64 - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5}.ReleaseVK|x64.Build.0 = Release|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugD3D12|x64.ActiveCfg = DebugD3D12|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugD3D12|x64.Build.0 = DebugD3D12|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugVK|x64.ActiveCfg = DebugVK|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugVK|x64.Build.0 = DebugVK|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseD3D12|x64.ActiveCfg = ReleaseD3D12|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseD3D12|x64.Build.0 = ReleaseD3D12|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseVK|x64.ActiveCfg = ReleaseVK|x64 - {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseVK|x64.Build.0 = ReleaseVK|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugD3D12|x64.Build.0 = Debug|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugVK|x64.ActiveCfg = Debug|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugVK|x64.Build.0 = Debug|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseD3D12|x64.Build.0 = Release|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseVK|x64.ActiveCfg = Release|x64 + {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseVK|x64.Build.0 = Release|x64 {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugD3D12|x64.ActiveCfg = Debug|x64 {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugD3D12|x64.Build.0 = Debug|x64 {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugVK|x64.ActiveCfg = Debug|x64 @@ -246,6 +82,14 @@ Global {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseD3D12|x64.Build.0 = Release|x64 {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseVK|x64.ActiveCfg = Release|x64 {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseVK|x64.Build.0 = Release|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugD3D12|x64.ActiveCfg = DebugD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugD3D12|x64.Build.0 = DebugD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugVK|x64.ActiveCfg = DebugVK|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugVK|x64.Build.0 = DebugVK|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseD3D12|x64.ActiveCfg = ReleaseD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseD3D12|x64.Build.0 = ReleaseD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseVK|x64.ActiveCfg = ReleaseVK|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseVK|x64.Build.0 = ReleaseVK|x64 {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugD3D12|x64.ActiveCfg = Debug|x64 {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugD3D12|x64.Build.0 = Debug|x64 {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugVK|x64.ActiveCfg = Debug|x64 @@ -254,62 +98,140 @@ Global {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseD3D12|x64.Build.0 = Release|x64 {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseVK|x64.ActiveCfg = Release|x64 {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseVK|x64.Build.0 = Release|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.DebugD3D12|x64.Build.0 = Debug|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.DebugVK|x64.ActiveCfg = Debug|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.DebugVK|x64.Build.0 = Debug|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.ReleaseD3D12|x64.Build.0 = Release|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.ReleaseVK|x64.ActiveCfg = Release|x64 - {232CBBB4-33D9-4445-BB62-08A7E7700147}.ReleaseVK|x64.Build.0 = Release|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugD3D12|x64.Build.0 = Debug|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugVK|x64.ActiveCfg = Debug|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.DebugVK|x64.Build.0 = Debug|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseD3D12|x64.Build.0 = Release|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseVK|x64.ActiveCfg = Release|x64 - {20401FAD-6022-8EB7-2F78-41369B8F0F49}.ReleaseVK|x64.Build.0 = Release|x64 - {0B41644B-B687-44D8-9CD5-9D41E0011561}.DebugD3D12|x64.ActiveCfg = Debug|x64 - {0B41644B-B687-44D8-9CD5-9D41E0011561}.DebugD3D12|x64.Build.0 = Debug|x64 - {0B41644B-B687-44D8-9CD5-9D41E0011561}.DebugVK|x64.ActiveCfg = Debug|x64 - {0B41644B-B687-44D8-9CD5-9D41E0011561}.ReleaseD3D12|x64.ActiveCfg = Release|x64 - {0B41644B-B687-44D8-9CD5-9D41E0011561}.ReleaseD3D12|x64.Build.0 = Release|x64 - {0B41644B-B687-44D8-9CD5-9D41E0011561}.ReleaseVK|x64.ActiveCfg = Release|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.DebugD3D12|x64.Build.0 = Debug|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.DebugVK|x64.ActiveCfg = Debug|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.DebugVK|x64.Build.0 = Debug|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.ReleaseD3D12|x64.Build.0 = Release|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.ReleaseVK|x64.ActiveCfg = Release|x64 + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF}.ReleaseVK|x64.Build.0 = Release|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.DebugD3D12|x64.Build.0 = Debug|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.DebugVK|x64.ActiveCfg = Debug|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.DebugVK|x64.Build.0 = Debug|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.ReleaseD3D12|x64.Build.0 = Release|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.ReleaseVK|x64.ActiveCfg = Release|x64 + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7}.ReleaseVK|x64.Build.0 = Release|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugD3D12|x64.Build.0 = Debug|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugVK|x64.ActiveCfg = Debug|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.DebugVK|x64.Build.0 = Debug|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseD3D12|x64.Build.0 = Release|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseVK|x64.ActiveCfg = Release|x64 + {71B60B71-89A2-4196-BFB9-4A848CF6C541}.ReleaseVK|x64.Build.0 = Release|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugD3D12|x64.Build.0 = Debug|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugVK|x64.ActiveCfg = Debug|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.DebugVK|x64.Build.0 = Debug|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseD3D12|x64.Build.0 = Release|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseVK|x64.ActiveCfg = Release|x64 + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9}.ReleaseVK|x64.Build.0 = Release|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugD3D12|x64.Build.0 = Debug|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugVK|x64.ActiveCfg = Debug|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.DebugVK|x64.Build.0 = Debug|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseD3D12|x64.Build.0 = Release|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseVK|x64.ActiveCfg = Release|x64 + {605856E4-34D4-40DF-B859-EEA3A7D52A7B}.ReleaseVK|x64.Build.0 = Release|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugD3D12|x64.Build.0 = Debug|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugVK|x64.ActiveCfg = Debug|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.DebugVK|x64.Build.0 = Debug|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseD3D12|x64.Build.0 = Release|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseVK|x64.ActiveCfg = Release|x64 + {8AB4CF3D-9824-4390-8569-B07776C4D1F6}.ReleaseVK|x64.Build.0 = Release|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.DebugD3D12|x64.Build.0 = Debug|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.DebugVK|x64.ActiveCfg = Debug|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.DebugVK|x64.Build.0 = Debug|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.ReleaseD3D12|x64.Build.0 = Release|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.ReleaseVK|x64.ActiveCfg = Release|x64 + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93}.ReleaseVK|x64.Build.0 = Release|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.DebugD3D12|x64.Build.0 = Debug|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.DebugVK|x64.ActiveCfg = Debug|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.DebugVK|x64.Build.0 = Debug|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.ReleaseD3D12|x64.Build.0 = Release|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.ReleaseVK|x64.ActiveCfg = Release|x64 + {B219C161-94D2-4DCB-A75D-E6A0906F534D}.ReleaseVK|x64.Build.0 = Release|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.DebugD3D12|x64.Build.0 = Debug|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.DebugVK|x64.ActiveCfg = Debug|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.DebugVK|x64.Build.0 = Debug|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.ReleaseD3D12|x64.Build.0 = Release|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.ReleaseVK|x64.ActiveCfg = Release|x64 + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D}.ReleaseVK|x64.Build.0 = Release|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.DebugD3D12|x64.Build.0 = Debug|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.DebugVK|x64.ActiveCfg = Debug|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.DebugVK|x64.Build.0 = Debug|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.ReleaseD3D12|x64.Build.0 = Release|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.ReleaseVK|x64.ActiveCfg = Release|x64 + {CA155B01-7528-4D81-B5CD-E801B4AA4027}.ReleaseVK|x64.Build.0 = Release|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.DebugD3D12|x64.Build.0 = Debug|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.DebugVK|x64.ActiveCfg = Debug|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.DebugVK|x64.Build.0 = Debug|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.ReleaseD3D12|x64.Build.0 = Release|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.ReleaseVK|x64.ActiveCfg = Release|x64 + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3}.ReleaseVK|x64.Build.0 = Release|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.DebugD3D12|x64.Build.0 = Debug|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.DebugVK|x64.ActiveCfg = Debug|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.DebugVK|x64.Build.0 = Debug|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.ReleaseD3D12|x64.Build.0 = Release|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.ReleaseVK|x64.ActiveCfg = Release|x64 + {99167900-C302-49E9-9C27-E6B3F666DB05}.ReleaseVK|x64.Build.0 = Release|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.DebugD3D12|x64.Build.0 = Debug|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.DebugVK|x64.ActiveCfg = Debug|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.DebugVK|x64.Build.0 = Debug|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.ReleaseD3D12|x64.Build.0 = Release|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.ReleaseVK|x64.ActiveCfg = Release|x64 + {80DDDE59-A412-4E64-880A-6B52697C967B}.ReleaseVK|x64.Build.0 = Release|x64 + {691F64DD-0941-49DE-B52E-949341192154}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {691F64DD-0941-49DE-B52E-949341192154}.DebugD3D12|x64.Build.0 = Debug|x64 + {691F64DD-0941-49DE-B52E-949341192154}.DebugVK|x64.ActiveCfg = Debug|x64 + {691F64DD-0941-49DE-B52E-949341192154}.DebugVK|x64.Build.0 = Debug|x64 + {691F64DD-0941-49DE-B52E-949341192154}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {691F64DD-0941-49DE-B52E-949341192154}.ReleaseD3D12|x64.Build.0 = Release|x64 + {691F64DD-0941-49DE-B52E-949341192154}.ReleaseVK|x64.ActiveCfg = Release|x64 + {691F64DD-0941-49DE-B52E-949341192154}.ReleaseVK|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} = {518F9E6D-D9DE-4557-94EC-F0F466354504} - {282AAB9B-2150-447C-9C27-62C38C23761E} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {605856E4-34D4-40DF-B859-EEA3A7D52A7B} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {E9189681-F552-4811-9B9C-C88E63D21363} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {8AB4CF3D-9824-4390-8569-B07776C4D1F6} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {152F0E49-0B22-4359-B8FB-BD76093D36DE} = {518F9E6D-D9DE-4557-94EC-F0F466354504} - {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9} = {152F0E49-0B22-4359-B8FB-BD76093D36DE} - {DE6A0005-923E-4007-B58C-3C35F690773F} = {152F0E49-0B22-4359-B8FB-BD76093D36DE} - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {B7D37434-A294-4E67-8420-AD09C54C10EF} = {CA90E299-AACA-4629-AA2C-E5DA38FFB78D} - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B} = {518F9E6D-D9DE-4557-94EC-F0F466354504} - {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} = {518F9E6D-D9DE-4557-94EC-F0F466354504} - {E6F10A52-9C29-47B0-8BAE-46C146EB7163} = {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988} = {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} - {0A6AC638-6567-49F9-B328-66BA201C74B6} = {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} - {4BD89013-BE22-47CC-BCEC-8A406C8061A0} = {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} - {613640EA-CBBD-4B9D-931C-00110D5C4007} = {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287} = {6DCFDAD9-C58B-401F-9FAD-04E9D4A7CB28} - {6D4D8D4B-CFFB-455A-BFFC-9490C5583150} = {518F9E6D-D9DE-4557-94EC-F0F466354504} - {71B60B71-89A2-4196-BFB9-4A848CF6C541} = {6D4D8D4B-CFFB-455A-BFFC-9490C5583150} - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5} = {152F0E49-0B22-4359-B8FB-BD76093D36DE} - {AC911782-AE2C-4026-9E0E-EB359ECDE830} = {518F9E6D-D9DE-4557-94EC-F0F466354504} - {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71} = {AC911782-AE2C-4026-9E0E-EB359ECDE830} - {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49} = {AC911782-AE2C-4026-9E0E-EB359ECDE830} - {232CBBB4-33D9-4445-BB62-08A7E7700147} = {AC911782-AE2C-4026-9E0E-EB359ECDE830} - {20401FAD-6022-8EB7-2F78-41369B8F0F49} = {152F0E49-0B22-4359-B8FB-BD76093D36DE} - {0B41644B-B687-44D8-9CD5-9D41E0011561} = {6D4D8D4B-CFFB-455A-BFFC-9490C5583150} + {20401FAD-6022-8EB7-2F78-41369B8F0F49} = {935D7586-B55D-431A-A0ED-338383DE1A1E} + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71} = {935D7586-B55D-431A-A0ED-338383DE1A1E} + {0ABDD59E-937B-41FF-B78A-7F47CBCCA387} = {350A0B15-98C0-45E3-872B-4FEFB47AA37C} + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {71B60B71-89A2-4196-BFB9-4A848CF6C541} = {4B8EAC4B-FFDF-4CCA-A6FE-4505631E51EC} + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9} = {4B8EAC4B-FFDF-4CCA-A6FE-4505631E51EC} + {605856E4-34D4-40DF-B859-EEA3A7D52A7B} = {4B8EAC4B-FFDF-4CCA-A6FE-4505631E51EC} + {8AB4CF3D-9824-4390-8569-B07776C4D1F6} = {4B8EAC4B-FFDF-4CCA-A6FE-4505631E51EC} + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {B219C161-94D2-4DCB-A75D-E6A0906F534D} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {CA155B01-7528-4D81-B5CD-E801B4AA4027} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {99167900-C302-49E9-9C27-E6B3F666DB05} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {80DDDE59-A412-4E64-880A-6B52697C967B} = {D16038A7-B031-4181-B4A1-2C416C02330C} + {691F64DD-0941-49DE-B52E-949341192154} = {D16038A7-B031-4181-B4A1-2C416C02330C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {357B2AE0-FE30-4AC6-8D41-B580232BC0DE} diff --git a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet.exe b/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet.exe deleted file mode 100644 index e75ccac2e86fbe53039dfab6741a455c34ad9099..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36352 zcmeIb4O~>$wKseQ1{`&CCJ7i!O)@bUO)zGd8E`&8hK~VEKnFq4_%R|30zn}2p_r;M z8Au$D)7sXy_I>QVrb+K(du{7&)0DJn3St>dsA6Lh(%6KiwmlTulEfNJsPp{SK4*pj zQIp>L`@Qe;dwX=B{k8VmYp=cb-fOSD&rtmE0WOx~I5}J>#BuHL>9Ml+^AH6#S z|F7Tw=$$I~J4Qkbe&@*acc|Qsk&7(8s;;V*a8GWD-Ntc6vUn~%!@oA1H^9ZnX3A!B zoE<64#MC$ntKl|L+$x4+I4+)|c)~u{hX^qdPb_DBf&o%FD$M+dM~PgKm*dV;;5f$( z$S5%B>EO6yKyG((T=S%?=x7J>tzW97Jy_ogNCpQw#!-lbN^g`$U2Pp+Ip4XV%f6~4l&D|` zlp9#90@?DQjHr?@i>)30*c#Cd2Zrm zAv@~vD0nsB1NPj^7hkOQ&mxGm{2#>1ME(Ka8{_z%7;eSzl(713Bco>qneH4d043@@Is+X@D;77kV+C#Oj4dXQHuf z1oa~sDh{^BQP|+LN~wIIJdl0J-#RN9$$@|s?_e9fFII!BXO|EniiQ$*Qk{W97Cuhl z;sc??7M3^w`h=%ZQy@S=F?nBC6f+$g*YU+8`d|U*wwmf7(GkHIND-`=kyfW;^sG$rw>b(M2(WJ&xnb ze|i$2V36=b291o^Kyim)2!kF#N}R9&pdqi|+cm64XO^gPZTjG1$cfW8~U77G1Y6Y?V-lK_K75hMD0^p1*n0$;ZNO}?-|9;6#02>oS#n& zABeg3`0!$EEQXxa%W4mTPRJV~D#(4XEdwD0xb8Lr;DjgdCSEAy62CpS)*hmkr7$|> zFJlmLgM2KgD3=G@+9PP5kDz&uLVL`dSFS%bq~>1eBs=Dh9!nCR|ebGM6i@au;fK!xfxg3a`ZB$FWT9wO)_Whg)r5=~k?SZyoq+n7MAoP2FP7&}_ zfG2YI0wpl+o}&=s$<2WP!39cWVJcoJk?;fp1SVX$i?vD~2&_l6UR9fbsUgUwG7)V9 zA!BF>fD?X&0)aLvOJZ&he3g;|fdssTO076r7%t&%NQqbrAQeIaq9uW^KF7dcBJfoY zFz{g85yZsW4kIMiwx2>MI3o}sVBgRC@Ky)DO6lMcLy-M(D1^SAA&SZ!5SaSJ+6**6 ztoAzG5#m`hh-%a^6V0F&1pRC#NMh)Q*^ODF~Y*0;R{wPleSgq0A_KpXWc zpp_HRxTi*=WN18bzSE(h#&EORs6DlN&~$V-h0)In^fN6rEFK6{{{yK)BaK_e1XMUn zOcj=cky7vuA}G)cSp6m8OOZ%;GH31EhiXKw3XiiO>papqB>}BtK@ag_BMVZe38gH! zn1Y1}^4(J{0mlsDtT8^rWB#wj{2vEerkwxF`S50cg zUuos|>@iEHONylQ_%6%cQp9)p?rmY9o)W=}sxemw^Akvxe7h6W!F(VCx)?saJD%Gp zDox^i#;uR659L=Y5bs;O3XkW1hOBqQbzus!44Bs8{D)`gg94CBh{v@t$#A3>H zuyJli^t}rHaAQ9mw|=sL?oo zLBbSN&En%h3?Fnb{f`fVb7D{;0{tbGlGqCXFk9iC5!$A%R|vz{j}Y@+QuCl`l71=m z2WpK0K!P@&P;lM(;gDDX`iDfKSaDS?drX}7X8o^Ml|Y>GZzo=fB%ZAZJSN*0 zlf$O{Hytehp7H!><{=Dqj=$`C+)gv(+d~TiE9Js;Fg~>1u0IuU$Xh#9d2fXh`yhGx zQxK=N4wdaK0Cg7>aoq*kTqv;+!t5;0aYJ9 zfNaklyyIw~px+ehP2ej=_?5#cXZg=iQ^?OPA}%9sVIKm?;Q@>MyUOP6}7>50d$2D*8_V!{snf2m-!0*G{7(k zBDWT~Xbna9cxtXZp0zd}jPvgjNO{dm9pk}5D)2PMW1%+^#6T8DIbw?2q zf!+xgK!*Wpn*v2+eJv2m7@jMsk0E`!Y(6@w!>hLQ@nT1v7CUOvyhcM%9j&lR^0Pi7 zKWgFBog_c&Nq*GVkRNq-!5R=;Jdt3#MDTi%;IbfEh39`peh!H8b0ClQ0|f_|{2YM% z?2pLLFy!Y*SbmNL3eJo2bCF*;BFfK8BtN|*KLV4VBf|GJBtJ*O@^dtl_#ArX#`5!* z8_LgR|E`GqtZWWWGlJ|W`Pr_P$S#Kngyp9?N`9s>`I*LAJB{SW3;98NO=CQIHX=W# zgYis$@*zL@6XhpglAnB0e)98z`AmLFuz6&2G=xaF9Sjh~X4-`D%?BqiVO(q?^I3)P z%)JoCQ4;6pL3nG2XO=^%LY&A>`*$0~`6X(`#boH)UBr3rf}BDFP}2S&F|@5Hlz0wv z6q})T#QAzc8@u!!w8Wvl5|pXnR}^8(-ueT`6Te!)?^3q?+B=&M&a|n$)7qg$a6>Wr zjtaK_KE@EKG1ay0A@JQrSdj7yWqb28MRM9gp8SAqENG+Yo5OAxoxv6z1Qk-h*s9?oH!*;`*#=l^Yi%b2p5QJO3s0Xh#{J~ z(H`H0QA|3#Euoa(phnyLdC7<+RiIJC8blLRtzQRaLe@O!XdS9nrsCu!tL=vTH=Py+H?P`B>0_}^@APw;!s~|tC2fa>-GJgzz0#*wDgYsYwV`(r) zNky?x=BULokyR_w{!-Sf`SSuRGE55ZZN6OzGH-%!w~<4)?oxs?zF^4rT4*R9L5VY} z)~^98mdk}8ocQuIMZp5_(i`U+m3eNlU&V==PSjE6ZrBneuW(qgn~!k;V=rWCu5+ue7=bDDlnJ^J&5scWgs`h6puBvw2Xg;8GM0+J;FD4cY{}v2^u^?L?6hd>;m{e|4|#drjWx=Zk#3ZXxqOx!H5vq0WcL(LbqH?*a04}odg`ep(n5!psV|V2{s0poXG5C zdlRnD9+2N^FX10D26C;~j5I@ZF+c46Hfo04?5^^S#;V$A%=vZ+GS_yhh6k1>$;8EX zV&-Z4-w5q3elwJ~3G4}Sa}W(B9!7Yd?M(uGVPt4i+a*=ogD@(A8k(;~!n?8WFGC_I zG1*Rf8<;7hxSzL?y`(>-6V%%4VZXW=2*d1;W@g)q*rT8Z?67#-Fm@KIC!a;-;@(2_ zpRQ0qQ!ukB4D=!|4GL1~_t*2%B#OZgynr0vq~`F!R8h(CZTRWIkw z39Yb)-h@>TIdK3Q5?GVGZw~FRt!A3fOW@e2-n+cG5R5!2pQL+8X05EqbC#?KbS@pQD_!p zY?~Brh)&3(P`!{HvUGWqfI5V&!n}7U=Vn3K{+;Acy8J|7qgEEeZP?x%WuT!Vu->M+-Drmeb^cJE{9YHJ81m`X$ado z2Ng74b2YrW?bn_Kyj#mJgZx_h+;am3A%8sokVYEmn5g!~4E_c~X?(2r(!dIj^;smThwqga}IoL)>hZPWc8knzt0As6U8TJnI zs|9{n|B#Xo2l)+szAH3R+0Y9HyuIbB+%vH!#35%pIX)EG@bgH+FM;Y9m8rh?CkQc+ z0Riv=6JOk`$_=LU_=|fy_kl_gIm}1FdKt)V2f2K44;E%ikE-poSQsTS#JY1cWf*3v zwl7lZYJrAsB@M}sH7eh8NJEFEobZh*w$%h4R`@=-vVEiFLwD|~Qfg)BHb#cnytcPh ztz>By>&A+Q+OeX^shMmV+8|9sN!;Lv7@|;f&jHrIK^*wOy-9=TksLjKHYkP`Vy5xl z$rz8BN#guT#m`Vt2*Rea0VdY>YKX`={x=N#DNWM}$sm;Nrul3VU_f7bZu}N?Z;p$=w*|WxGf88 z57!Z`R5yS=R1HbvcuA>dR{4lhjZN9OQXPyZ+dLnX6xm=t+W}8*ANKiC<6YF}FbgH& z3x?}wX#%D-j;uTQr{YA-2sCjD3`(A~mU)B0kCacMp zPRKk>e8D`P>3}G@vF+`hWsl3-F1c3l1xgexFs5{V!}6QQEj#6wk?m=G!QvF?Yq6*A zMbn#?#DKXnbbQQ_B}DkZ|cljK?gM z(4o3NuRZD84 z$T5PK9Me_4IF8=His%G1l;0#^|INRqLyiQA*5Kwf=$u z5yNxG8J-J<50Z`n>Qt>y5y;K{)k&DtcU`YVT53@OHRi;pZOl$1>o*;r8`X%%?`HjW zJD;mfDNYiJr(T;wSi8Z8nL*DCQ7W~3i(KqSe~TPldCh(lwbroo_hS8ibRItkn=x_I zR>ALB%ukCwfwd|gYADv|OW7H6?_xe%91M3`PHc}el^D0(X3C~+&o=Ve#uOOy`0RO( zjXbA%f$MUx?VnTy%jCIlmPR>;v zRY38^{*Tt>+xXo^%bRX!2h{8Nn%GxT&iIXp(M&0V+{f?M@~VLRCa~kS<$RZ4#g}Uw zP}^6?{4@66u}-Q9+>`4=SWbayg_1X>m|{QX&zv3fUDmj#y{LiM43WxzE1kczW1V{aYal5)nE z8cmQSoubVSQe^Jmu{ z2kjNYov@}t)d?Gg$AuXo$b(q^fKd6DtID^t^6z3>ex34I&wet#o=v&lN^9XivFwiF zh(5gRN>>!tlnccYMH3G(B$iBWA2a#E1RHQ1!uhjkRufOEW23CH(kXQjwtZ8bQeVZ^ zQlSUCa1?*P&@*hqNy0#KOOu z?78XP@@4FVLQ*lr`H5ex_7{(0p1|&fdR56$C+<*V6>I%5WYY4c>gn&nZ>PbTaw2xr zw}V-S#lf#?{SuOKtENKra=aaz8;VpbNPyA!M>arbAF6>7EQTH`*LE<`LP}- z?_(*iE|0w&ct|$56VnxM8x0$)9Vz`J9byw4Z1W_BBYiHmm*!*aLdw;av+kL-9;W2= zqFc#gR>6N>ZP}GDAsq|lZ&Et_wlUtOwAjW}1(=U^CHQxZ^1G5OyGH#srK(`pXi7gH z^lwP|sL#>`JlkhRtLDW?JnW%oQc(V^9}#SlY-8?+e7lmaY?}bMA<48c0ma?wRUv2` zr-fHYsEZXt^8gDIigb(=eP+}R0Fbs?Y5CYY*Y|nWGoK>>)sv5)!}vLaHSjDSdLCn? zX7F?Tt49Go6zlhnN=bM4H;_?`l9W(sZnJ}{v7Ojqp~din*Z@fPj-o5Tsnu*8%AF=T zxjBZ&NQmvKKp91Br(L*$T?B++aFlBQSmk?yq(nTtoW`RoP_ji!9CiRC9) z-rBx{- zXN+@h@NZxMt=3)O!uIu1rNp3gzB$HkP{y8(?TLLY_KYz076cqTI|+Lk5yvW_pqh_Q zc@4x=1eA(FKXDcU-CdFL8sbAcMPSC%DBP>~)d_@G_>W0g855%b!9*;HfrPoStCay| z66o+uDiNV0+ygSj6xFV6sgyP+1f3Z6rj&ln@jpR#ZG>4zfjdv6DT}GkL{gclJ~VM+Fzrro z&&8QBOjg2AX5dsMc46~fY;1m|hxORz?6L}0L#KVw5Y(`~Dj(=l3<1e_H5y|jP!IET&CC^Q#5dgi@^T>n=Dg`5k zxbx?5ODAv#l|{Hi6{Gm<3YvLV3E7(KFo?=UF-RHNu1E3bzabxQMkFCxJ~|_*Sh%nv z6)UmQbWT`iDgqfZ=opI*bf)6gZL4seEVN(?E%M6Ax+2V@4*2aU= zsEtryJg%wbVam2%B@d^tvB0d>RbpqO*)H67$8{P!g;=op44M%xQDAsRuA9u8aNT4! z$iy7-DX*N&#GJR04QM&%pqo0;8%}IyEBMEh0ape;&C-pYdT^dU$BxY_EZ;JJPMLox z{|cWoFXgnaGZXu8WNh>+qI6Vfeab*Kndj#4C3ft-%lx;de1P3EHe1 zE!3>NK-Ts)->+)hNE&LffGu=Nr)U;lJ8l*(VP;`L4j!-;kxlqMrqT+ajcOya@EI}- z%cd|3|6Mc-pPghDerKXt7?^$}6S}Y-@9P?hIWCLZpwRIuyNi4`DVelcq0K368=1x6 zF5z~d!qG468&~M6#^p&@eJ-<;@2IN*{4Bca12uSsm7hhoeQ;PU-Vaz2z8@f6ER&WL zj1S1q%4qpfOu3~cqEv04fUosd%!;u2h7vo_tvI|PbAzgN09oQe{P<0UfNdmbW0pdk z5a8&Y*$xp@N>^{jPvN0r(kVP0U#p?MhR@+graFhu&kNXw?KqB?thk}XJR+n=+7t=` z&9BiH7p6;qLoBbZUAxPE{2UR%jwcQ$@29~)t|RU2QN5kmhln}(B2 zA7s^;PF5dv>{;At!|~UOc?jPGV+c&PeNTMMt`DXj9p7ITj#+vl`%B2{`2KQqe18em zekqtrCvf@7u)SqoH~=C^S@hsZ;JX!3f0cY+B+A*@t3Z0aJQP?AIfi;|Pc3Z(B9PC#X&?xJr*q94dV#0_^X6l6oBeDO}_f~9G}(OuQ__WE2p~B=XZ|a<_ac7oaBu9&ZtP6k@KTN3$bx)|IhD!_q!kc ziGTI%;MqT0UfFgFT?r2bGUh__?B5pm3l=MoNy8DmrK^kUM84iNiLVyujl|bAcl&-n z;LjZ!(%XfFVjAB+bPr7hHiZ?`*y%oCDo6z2nhp&p*oUPzD2v89qhiuCo-jO0c z(+-p9$2i6s-!J|*@9cJBVga#HMxF4TU1_2<9YJpMFafP2OXQXB1p98Dk+iClrmO{h(-wqgMj ze){261V@b_&?}xN1Z|_lTYNb3(q@!<1*ciU<0xteifOMA3TAy2;rX0|lpMMXCY8ee z8t|rU^|oX_J0T@oZOKu)<^u7m65-FW^aa&v;l!^aNQlr9{uLn$gi_Z39?uf_x5N>p z|7oU@4~!uH>hnCV7zuo=KE$t5`*&UNuO7zLLq2bAGVhtIY3taTK&MQe*?wEU<%4b0 z;H7+kdv}-kqPa%@hI3&PYxr&~9}C;7Z)cJc7oGW{qzvTGu)In);q9~@#{#!+=UgQR zt#YreUs`YJ3y5FPS|TcZbV)XEZo+~N(lgICURE#X+0E%!#0$CY)0WfJ1UB!w5LPij zPX(9}`>F_nWMY75)#knDm|+_S8D!YTEEfkm1=+NZI`^;NCft@BygUa z@X;nEPMRq%~D|jz19C@MDjg-RHmH_~q641q(WN6Pp+rR+ERjtNr#b<&8(4=h_ z>_OXw1Do;f)W2gHM`9H5=8IcX8;RpI`}kmQ3Eh<3#SR(hA|FQQQOxc~oqA-zZu1@1T2OsiCX~_s!-+h zHJ7q{Y;fKmLLu@E7es&f_o0wD25xW%W``WtLz?mJiqIDa z#+(Vg!H=RNZ{k(;GCmc_FNTw&mnxe1%ZHN4a`apQ#l zf=AiKVz%ephOWT^gi(%?N6?zJ>?3PBYozrlK6x`f(3nBrMfX$YT4~IbLz-_xLBTW) z%acHxI-waaenhw*DfKdcywDlLdUGSLx;4E0o^U_ZxY{ihVA^s_cfuG*u?z3bB));r z2Zd22uFpXZnBamOQf*-N0g{Ttf!xB|SltUfR!+Er3^e3jEH-N~>rO+-w*jVmd+&xn`J7|uUgYex zoqwho{iZ*aZRzx!2d&HfvnZif0p8S(;c!jR6I$xyn=qG>zS|x`zYqQcui8-n)D9EW zGzJxnqr%_~I%MBls0pi$@B-p%g;l7WAJKp#723%g5d!e+p{2-u|0JCND3NwExP#`#mkUoI z7heU&3135iDW4BgrE8^D@ZCe1&`PnPfD8|m*oC_QwidU}oC50xTB)!Y3sxlW+4Uz5 zh)NL*m$nTE@1d$L!GjJTd>VX#oI!;z=L<7pdsIKkP6(7F#r7c(|H#Oq*bgjkc;> zpm_HoxOQmpxQvYjx}t{DR458@zAG~CPT^gM4f^^%4D@?yTYygZJQY9^Fb!4Iwo+0p zl0NERdt1kqj-foZdk%OnR`>?)`~{V$*Q~|YfIXqtdh~HLhW)mSxW|Es;@jW;_AjWs zn1CWkJ1#>iGcR=2#|b7dv$tMRTT2q00Z|}HZ7+TpSFlZ(0<4^O!!sH1p1|z|D@Q*> z;w^e{Cd8!1v^R;em!S(@q`Z_)%(->w7ACq2eJ#n{&by?}J1!f#6ZemKi2V~rpzTl{ z?BqMC=OqALhY*L#iL=6KFcYm;(NVg0y1wH@>W`;I?(NAWhI5|yJ=h0(Z>DMHQSgua zI{0hXN?c|v;=vALkMDOhi@nxEYQr?)Uce2_6ghn>l>uKr*YQ!`-8Kd5Aj0)i3_3Bn zzmTR=0Rs-CLGw$LZ?aN#I0r|dcDkJF2qi8?5KGb`cnrl8!^s$1^|uOV#HPg6$E^L; zz0V#xh@O~rklu+0=zaTsdZ+EBcV;WzHTx5fGjG;&%)5OL^Wt_gZ$S(5?)Shu{KdGV z@OT^d>|y51pFIK}hOlRkQM~OuK8g><3243y|DK`DXP-kX7|1M1+(RaNDs;=H>yYzE3*C?MXFg5%IzBeRSe+=kfpT@2mk)~ z&u|EbkW6dNT5+vuxE!_C(AdS7#KjkdI&^YvtB06&(JnNj3@J=f=Hrz4QaH0Tl9}Lh zUc?v~6@L_?yy6!t{%HN#%aFhj^txGX-Jac7fhtmniC`Kh@0%XxwKX&E+YQY7UM;-CUu-)JkGIMFZ3htuP~ZpxK}G?IKW`nYxBJ@;u-v2g zIhef|pC|xYg0`cu;#h;WW0=%1Q9h3!aRKAX18YJby&bR}#Y8H$iFR9{6ueo{{TNHY$xnyh8q6UHj{)&cJVUyB-7!Fg*!rY7zy zb3LESyo-+M-CM)+ddg0PKz8G5u4Rt!c{=6L@ z=i~d0Zn@<{*Po%QcLDS;fzoRt=m+?ffeE1Fwm%BhegcK8I+R8Yp!>o8hGcaBE@dFR zYlP_I=V|;-A>61+zg-Q0`WZrD3`wSV9_4e6K&|u5qO2JGsqzZ{ z?NlbPtG`tBE&juG{%QX0a{o$&ze365i-O_{Anu9x>l&TQdD}o*qsQ4Wvxm2xua~72 zI2*k)d)Cx*Yp*Kl%|+P-BUvF;W=Fh?^W2@eDL#ht%*$*hFEP^|AIo`WBN5&-B*t>y zoA}H=(BV5H^RvKEyp*tObQ*616);2&Kmz@?;hnnrX>0VS#99Lz`u(wfx7`1b!e0uK z&wqy|Zfv>1Ue9#jnHUkBW8-(|C$v0U#g@y1anx!+a!BVCp4H^3JuCF5RI)x&7jTKl zc9Ab0^$o=1Uynvi$27ySG<^qM*JATW z0zgQpLkpCZkm?1akm}Hai|jqZ-ec^&nB{8O+rZw{?A^lNdF;K1y_?y)h`sIXUB=#P z*n2&DKhNGr*t?d!8`%30dwbYBgS{8A_fht4W$(T0y`Q}gu=hdsP6nOFWnI0Y1qp~p zj{v?bSrN!l@(*GH7ruwDUfPdf2o0(*Xc$<4{Z!K6-+zKhiv6O|;0#>J4VT*2fMpZ~ zJt!!ms3#OB2}LimdVBHPJA?f{N8#fB!9M0680=ww$6yEiFs29h1I_&rGF@pqXp z4I!ckEjWtq#u;yD!A=%HqI~f8z2k&GD4>=OY{3oE-grS{OqCXj)y19nnwFKXD z6+CM{=`CR@dQl(^1*G;<(e+d`AQpwyZ?NZw7>$$UWEjP+{1w1^33yYao(SFx0QfBd zyhH$5VSq5dU^fWM&!7?Xj7uP~G#5fYNf-V@K{tPqlJU}nv#Hc+9s_$SqKwoUY zR$y>7($L8Nm!G7kInDs5YvDNMUaUO`pNH#)I}Z0XxX0jX;8wwD07K8w9US*pIOAj3 zI>L3pai7QUMZ)cY>w#13L>z86oB+3K7stH>r`V0(frQ%!*9{l{IL8&j?T71yyLk`C zt%3U%+yLC-Cs04!5xCh;0v65#w-@zqhSS2`2KO=AFaY;5+&;KwxN}Nom)3=G9oa9|MMV zxU}=tY{5s$D8C1;8hN*tiQ&za)tbr%{3K_kr>?0HvNJi)<*MAFX>@MaG&mc#cxtJ- z)s2rdHf?XzI9sZm&0;Z*@Kja0J(QncS=ZpK)_9sUP0ddF1=(h=M^l3|%>p;&FN4b< z_W;M`RyHoA%tnx*nZLk2U*mRG5l;HySx>E#m0O}=KTs?FM(yUx%?&&7>$FvsUbj=@ zZrbW>YIJI8LF>Bw?4sP2s~mayyj0y{jT9??Xalj7OI{Q%jI#Hx=hl}td0kb`^=fesZ0a8_-(0P4~j~12=FFd^iK-PYqvvx0SOZoDav%hr7$lX*KZSI_`vzXAPVI zGTKmN<<0|NSXL>Igl+_BBdJzy5pXPlJFo;e;HZwi`>mWAex#1YNW0I<6(MaE+!{D} zy_H)J-1IcV_0(bZ5T*mm|0$z@3c36^pcUh+mk5kKm{tdZO_~mz5*W zF4a$Xro**BR-&Im%zvX249OCek$)LHfV_pDI0WVa<`B{e4?WR!a)8~5ys6rEB&t5b zqeb7WN18#ZQ~o8$AHz5i=KZK1~8N1!?4r`N09F!JW`v&{#5mZ%Ml+vXonKCQdkWa zUGHAhOHVT#>l5&Tcya*#RD6D3f~PW+zX!C0aZ}ngv<~!v@mm|Jg zuTg(B>N_+AuZ|)8dc5->{{q_eS<4b_By-X7MQKDI(Lzrx9Py51h2(~M#rdMJ(ZPXhKl($;`a(GS7zMfx$&rjRnC`Kl(_ksiXc6L<{d zqVQ8T(Pxy#6UodBxZ{XhrL<_-Ko+CZ3ZG;nx((5FQyY$<{_Dv&)jI+lVYyHs9*u+K zA^~oywgZ&=qyl3EM@4VIY1UyrgX1vCFN6!g{RJ**1Nsi`%Wyw|`x9K!Mvhwv*9`X^ zxIwrjSaa6Gtt!nc-O-qP;^~8n9{OJ1_rJB`VeK0;fi~B?ezn`_a<8v+*KWt6ygsj~ z%DWZHgL{2Jou|;dSz7T6Dz`efH@O~hOY!yj_<4Vp64nfIymbxLrB$xFW|WJtS({omN7UD(#9}&!hZ{3UJd5qxNtn#1#svO&~>RC`+EV& z?iT6q0g%pfEK)Z4vBw^(-V7FU2b!ezxja?LV)M9z6Ve`LbsvgOD`RZn4o^rE;f^5f z7}3NkPG-A2`r8UUM3dN;It00dZKA#)=y;H4?5z;S=ti@M%4G8a}o2%Tk_M7mh!#@h2#`TXR|7FQP1)u1mI+TxE@xJ;Y{6v#xy$DP78m|Ex=8YY&^lXJs zX*(qU$tWBtgyUCI*|#MBJqZWl?YIFB^#+k<;>qw0U^fAlo?Y+>w_hqt;YBVh z-d8_i-W-pDExQ5iKosnrYrs;S#B=pVk$#e^V>f`c0+z}YN@b{@22h5|a&9ZVBTpDV zB~TgiDUF`6PvyuF?i<8QP@v0pnUy|Tc8a-j3+O9!5@oFLb$!%76Cbs7mQZsr~DDMl{_v1^w1OD zGezUH14g}_WwX~kSVx3S;u;f9EkqiJv`yQ>DOg&b#5{wvq^Pp_KGcb{zNoYnPhozm z#QGbR)`+wsq~%4W9YESVq}ij=UO?I+q%}mPy@9kmq*X_yT|!z0(x!U;Whegshn={U zgZf?z#T<6rZB7c8HDz<9g+&$F_T@{e8ydLjaivaAKCH0W9@zCZdp%BfHpe|=TeZSg zq}MSJ?%P~eR;r_@sp^qBkJXLe3$d1D+pKG#*=%-jZvMfh|)3c?aX|rfZ zZg5)3o(zv+$j!EAms82QtR&FIEd8SzDYQApOjZ$+2Z+mq(}}dHOqAw7d~g_StD6A>i`B_m%FNmPq*~k+C=ADl1(Z zn^n``b=Q98(%ISNq6jY}33k`k)!e8@N^)$4*$|v@VY6rFrDkJLkg#&UgnT$XOb#Q& z)HOjEZXg_M3LPt`yfV5!Z&)zglkbR9`Pn-%+g3J=7CS&+o@L8zy;AY zecC?$r)s(-x+ZXvjnfS{?8!W z1rDT!d+3I;v|5@K{L!U8SpI1YuVWnOMnIA8dn6sl=2^7$aCEo&N+GYqE33*BTmczGw-p z`!oYPET2zBm;6*yLb&8~Syonhn1!FJBvyQ^F51+8W=7c)P3NbZyCg!t8Ix7nu)T7J z+tJum)zrAnDosibXe*Ltg{g?CWLzrRlmjhFTFArt;c}AU;?nY5d#XVz>XGv~2Na#^ zaox^Q?F8SOc5nsUR_E5OP1~H@U012Tz> zv@kn%Ar9{woGv}>mnpHV*44D#Hhv|cknmt_<+wCXBE*4Xe=1{eR5gK!My@<})#?>x z%ZqIh${*)yFovqIm2x;Lt6UyZ_#8D*R=Mq54bvI0m*Z-x8k*crSj)H?jN2;C%sHgP zaYu6{8QDQH7+Tj@(}dkE<3(MQRHKnLf?REAhq0FZY$Ht$+lYKa)Q1b&f#{} z64}&jXNwbkh!l?lS0o&s9nDT|3Go0m;nZR)wkBu|2RvX%V@=%_ugmFZtc32o-BIb< zf;~3+s}8DR9WEO@7RhopZmV-OHPSH${ZU&+tppV=9Swq?GoxG*9co)R>~P|yfwQF! zI2MppAW$aKT0?)~n1j92;b?ZC?_FEKQT&msa13XUH@;`#h&kf8s%);if2;d`946h5 z68B^1)-~PVka~aW{o;V0l#jm)?Wx=9ybc5jw0j4*vNalrn}ny~gmh zX>#Q|D=~hYc9+vlg1Jmy;&r-q*qtt13W82i<;-mYuTc3dS7TpZ&D|Q6iunuDgGBCk zlv>KTR|Ym`yU_qAg}61b%OeA$vvlPrSyr{@XO#azU%jSDp}TQ*i# z;%=}rbKwrBdtuhHSu-*!-EQYrx}psPjqc2a*j||3RkhBomG1ku)>XNh+)Xu}`=Q*L zE8Sa{Y)f6JfudAbgZ*K7bZtPT(PVgBUbkmCmM{t1{2O4?&u8UO64%JR*gox$;z)5h zAN8UZXSLl`w+%}SE`UceqqA)-C`t-+5iX23;NCmlnF}l3%Nw^fJ>qmN)OhQ%t4KY} zTv$`t;C3$5JP^T?@xZlf$#`G_O&JeF8i(Q;4}|%K$fUlQGK#@|9vL$2P1+P~zSf~_ z*0yT@m-buQ-)sM_9n;Q9ou68h+LHS7)b~?Ub!9rI?or)ix-aSabbr;w>2J}`)8C^{ z*W2`4^k3HBY{)Y_WjJK`sbSb~Q+iXncIlUwUS3*Y>@$uTr<>-O?lP5|s!U%o9Wi~+ z)NWd4-e&od8Ry%g|(0WNgZKD&y-J?HMm;oX_}MMttVYnVQT6nfGTl zWWJDjHuH_lcQRv_%~`f!S@yC`%f7zs$g=KbZ!Q~Mc4gVTtOv64vsPto%5r9H&3ZQL z^{n@^I1TNr@ok8@aP8;T@ zZAjzOewfyt_RF+ir~NkVX!;-1|DOJ}rEx}^@dM+iQEhtIRBzg5YBe1;oi)udFEkgL zSDIIwA2qj{|Cjk0^O$*iMqcKZGijh4M7?h9kFWzOg)kMO6q88 zoGx9LtE3*a8lkTD}q-)a~3}!=-VU;0Z_=@2L!%K#BX=lJ7lHT~C1`7w~#v)VGp@`B|h%UhOrEf*~xTfUdEEwe3i&a$n`re~?L zDziSH^+eYGtQWF^S%1mmtm2)h8CspT0=>LV`vvWPXw9hwsb#4SQ1xqFqJF;q0lisY zuHUTx4}H7-KEoEnal_9IuNYo8l&96ErKcC9|1AB(^leM`E{!)nZ2YS6XU2aRV@w*; z{iY0XWv}TO^y`mIFPR2RGV?9wB=aJ(5&U`Fe8Bt-^BME&=2FXAOQXeOId4&BJeILH zEtKzD#XqdS*suPG(`|ip;Xihch>2c4n%VnU;NHS#VkZvfnOyZ&_@XA}cWq zOts@28}rN-?N_w#K|&R&38~4cX{qZ{n^U)^?n?b)>Niq9N|ozUbb4KpZilW_7tkHj z{k!fLx?k#E)t%M7sr#+&f^JCnf$p-7(@)dS*3Z%30U6fmEqbdyUvJkxtgqKUrr)Rk zivGC1M}J!Xd;KSRnPH}3zM<66jQ%-lc;3)u=rz1$P^78T7NspoD^J^*)&|LWDQ!#o zQ|SlNpGp5g`bhev^sJ?MOY4_5Eq!L`%S&e()yBJxsm3B>h4EdZU>q?nHRYR%O+M3p z(>F}dfpagJx=p>NUz_?(@0c!_{%jgCT{4ZCV$2HjY;%J7HnYZjk9o0K2c9l7=b0Zg z+s)V}8uM$NUsH{X_HL%^8*tEjMS(&oE`wW_UApXZ(9cPsZyR?_`8B z44JDkTOcD}$b2sIhnc4`-^;w5X;}8)vdU#oF8kTCPnONkx;M+3wL0sOEHCkT6Yg1n z*Fx$@>cvz}C)a6o$+|_l#X7Cdpfl<+bXHxSZjEleZj)}m;eg>F#{OZ$5yNxf(J{!* z^M-ava);rhp~rB}&~G?z7%*Hgj2JE&Mh#;IE=``MNK=A`328}b^U^eF$>8SVG;Nw8 z&6<{%HkQVvE7BLGFHX-$x26}SZ%S`Y_oTO`?@iyIelY!5`b+5@>3!+v()-iTr(Z}P zOII$Pw{-DR?NTeaUbb}2(&nX}rTdp2UV3Edb4!mcJ-M`J>A=zpOUIUS#w6oBqsEwQ zTx48qG@wW9#xmm?<9cJW(PL~e?lJB)?l&GV9yA^`9y6Xao-_6v&l@iq7eP5>6q!b>3PgizA<6aOvd<3Iiq}6JTTC28DyGFYcGx&b(LG5AfbC~s>*S@4Z zsqNLC)1KE}(2i);sq<1ZQu9)aF!DF0?n&L7dH}Q2bEzj&`_Rv$=w~H**oc0u#^^bq zJFaWjoz(T})cRz-L7#^a(yZU9Kde8>#z&uC(2wZlh6IDgU^T2aY%=UL>@n|Mj z2MOpv@At9(A3)EK8Pu$|lhM -#include -#include -#include -#include -#include -#include - -bool ReadFileToString(const char* Filename, std::string& Str) -{ - std::ifstream File(Filename, std::ios_base::in); - if(File.fail()) - { - printf("Failed to open input file %s", Filename); - return false; - } - - std::stringstream StrStream; - StrStream << File.rdbuf(); - - Str = StrStream.str(); - File.close(); - - return true; -} - -bool PatchGroup(std::string& PropSheet, const std::string& Group, const std::string& GroupValue) -{ - - const std::string GroupStart = "<" + Group + ">"; - const std::string GroupEnd = ""; - auto Start = PropSheet.find(GroupStart); - auto End = PropSheet.find(GroupEnd); - if(Start == std::string::npos || End == std::string::npos) - { - printf("Can't find a \"%s\" section in the file. This is probably because someone deleted it for the property sheet. Revert the changes and try again.\n", GroupStart.c_str()); - return false; - } - - if(Start >= End) - { - printf("The property sheet is corrupted. %s can't appear before %s\n", GroupStart.c_str(), GroupEnd.c_str()); - return false; - } - - Start += GroupStart.size(); - PropSheet.replace(Start, End - Start, GroupValue); - return true; -} - -int main(int argc, char* argv[]) -{ - std::string newline = "\n"; - - - - if(argc != 4) - { - printf("Usage:\nPatchFalcorPropertySheet "); - return 1; - } - - std::string FCD(argv[1]); - std::string CSD(argv[2]); - std::string FB(argv[3]); - - // Get a relative path from the Current Solution Directory to the Falcor Core Directory. - char RelativePath[MAX_PATH]; - PathRelativePathToA(RelativePath, CSD.c_str(), FILE_ATTRIBUTE_DIRECTORY, FCD.c_str(), FILE_ATTRIBUTE_DIRECTORY); - - // Construct the Solution Directory to Falcor Core Directory. - std::string SolutionDirectoryToFalcorCoreDirectory = std::string("$(SolutionDir)\\") + std::string(RelativePath); - - // Read in the Property Sheet. - std::string PropsSheetPath = FCD + "\\Source\\Falcor.props"; - std::string PropsSheet; - if(ReadFileToString(PropsSheetPath.c_str(), PropsSheet) == false) - { - return 1; - } - - // Assume that there's already a FALCOR_CORE_DIRECTORY property value. If not, display an error. - if (PatchGroup(PropsSheet, "FALCOR_CORE_DIRECTORY", SolutionDirectoryToFalcorCoreDirectory) == false) - { - return -1; - } - - // Assume that there's already a FALCOR_BACKEND property value. If not, display an error. - if (PatchGroup(PropsSheet, "FALCOR_BACKEND", FB) == false) - { - return -1; - } - - // Output the property sheet. The file is usually read-only, need to modify it - SetFileAttributesA(PropsSheetPath.c_str(), FILE_ATTRIBUTE_NORMAL); - std::ofstream PropFile(PropsSheetPath); - PropFile << PropsSheet; - PropFile.close(); - - return 0; -} \ No newline at end of file diff --git a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.filters b/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.filters deleted file mode 100644 index 2931a7b9f..000000000 --- a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - \ No newline at end of file diff --git a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.sln b/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.sln deleted file mode 100644 index aae784906..000000000 --- a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PatchFalcorPropertySheet", "PatchFalcorPropertySheet.vcxproj", "{74D48FBF-0999-40BE-A829-5F60BE1985DF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {74D48FBF-0999-40BE-A829-5F60BE1985DF}.Debug|Win32.ActiveCfg = Debug|Win32 - {74D48FBF-0999-40BE-A829-5F60BE1985DF}.Debug|Win32.Build.0 = Debug|Win32 - {74D48FBF-0999-40BE-A829-5F60BE1985DF}.Release|Win32.ActiveCfg = Release|Win32 - {74D48FBF-0999-40BE-A829-5F60BE1985DF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.vcxproj b/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.vcxproj deleted file mode 100644 index 89e1d1bff..000000000 --- a/Framework/BuildScripts/PatchFalcorProps/PatchFalcorPropertySheet/PatchFalcorPropertySheet.vcxproj +++ /dev/null @@ -1,87 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {74D48FBF-0999-40BE-A829-5F60BE1985DF} - Win32Proj - PatchFalcorPropertySheet - 10.0.14393.0 - - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - - - Console - true - shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - - - Console - true - true - true - shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - \ No newline at end of file diff --git a/Framework/BuildScripts/movedata.bat b/Framework/BuildScripts/movedata.bat deleted file mode 100644 index 298fe5b50..000000000 --- a/Framework/BuildScripts/movedata.bat +++ /dev/null @@ -1,37 +0,0 @@ -@echo off - -rem %1 -> Falcor Core Directory Path -rem %2 -> Solution Directory Path -rem %3 -> Project Directory Path -rem %4 -> Platform Name. -rem %5 -> Platform Short Name. -rem %6 -> Configuration. -rem %7 -> Output Directory -rem %8 -> FALCOR_BACKEND - -setlocal - -SET ExternalsSourceDirectory=%1\Externals\ -SET DestinationDirectory=%7 - -echo "%ExternalsSourceDirectory%" -echo "%DestinationDirectory%" - -if not exist "%DestinationDirectory%" mkdir "%DestinationDirectory%" - -robocopy %ExternalsSourceDirectory%\Python %DestinationDirectory%\Python /E /r:0 >nul -robocopy %ExternalsSourceDirectory%\Python\ %DestinationDirectory% Python37*.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\AntTweakBar\lib %DestinationDirectory% AntTweakBar64.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\FreeImage %DestinationDirectory% freeimage.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\assimp\bin\%5 %DestinationDirectory% *.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\FFMpeg\bin\%5 %DestinationDirectory% *.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\dxcompiler\%5 %DestinationDirectory% *.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\OptiX\bin64 %DestinationDirectory% *.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\openvr\bin\win64 %DestinationDirectory% openvr_api.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\Slang\bin\windows-x64\release %DestinationDirectory% *.dll /r:0 >nul -robocopy %ExternalsSourceDirectory%\GLFW\lib %DestinationDirectory% *.dll /r:0 >nul -call %1\BuildScripts\moveprojectdata.bat %1\Source\ %DestinationDirectory% -call %1\BuildScripts\moveprojectdata.bat %3 %DestinationDirectory% /r:0 >nul - -rem robocopy sets the error level to something that is not zero even if the copy operation was successful. Set the error level to zero -exit /b 0 \ No newline at end of file diff --git a/Framework/BuildScripts/moveprojectdata.bat b/Framework/BuildScripts/moveprojectdata.bat deleted file mode 100644 index 71afa8669..000000000 --- a/Framework/BuildScripts/moveprojectdata.bat +++ /dev/null @@ -1,27 +0,0 @@ - -rem %1==projectDir %2==outputdir -setlocal - -echo MOVEPROJECTDATA -echo "%1" -echo "%2" - - -IF not exist %2\Data\ ( mkdir %2\Data >nul ) -IF exist %1\data\ ( xcopy %1\Data\*.* %2\Data /s /y /d /q >nul) - -IF exist %1\ShadingUtils\ ( xcopy %1\ShadingUtils\*.* %2\Data /s /y /d) - -rem deploy ray tracing data -IF exist %1\Raytracing\Data\ ( xcopy %1\Raytracing\Data\*.* %2\Data /s /y /d /q >nul) - -rem deploy NVAPI -set NVAPI_DIR=%1\..\Externals\NVAPI -IF exist %NVAPI_DIR% ( - IF not exist %2\Data\NVAPI mkdir %2\Data\NVAPI >nul - copy /y %NVAPI_DIR%\nvHLSLExtns.h %2\Data\NVAPI - copy /y %NVAPI_DIR%\nvHLSLExtnsInternal.h %2\Data\NVAPI - copy /y %NVAPI_DIR%\nvShaderExtnEnums.h %2\Data\NVAPI -) - -rem deploy effects diff --git a/Framework/BuildScripts/postbuild.bat b/Framework/BuildScripts/postbuild.bat deleted file mode 100644 index d6fa41ffe..000000000 --- a/Framework/BuildScripts/postbuild.bat +++ /dev/null @@ -1,37 +0,0 @@ -@echo off - -rem %1 -> Falcor Core Directory Path -rem %2 -> Solution Directory Path -rem %3 -> Project Directory Path -rem %4 -> Platform Name. -rem %5 -> Platform Short Name. -rem %6 -> Configuration. -rem %7 -> Output Directory -rem %8 -> FALCOR_BACKEND - -rem echo "Falcor Core Directory Path:" -rem echo %1 - -rem echo "Solution Directory Path:" -rem echo %2 - -rem echo "Project Directory Path:" -rem echo %3 - -rem echo "Platform Name:" -rem echo %4 - -rem echo "Platform Short Name:" -rem echo %5 - -rem echo "Configuration:" -rem echo %6 - -rem echo "Output Directory:" -rem echo %7 - -rem echo "Falcor Backend:" -rem echo %8 - -rem Call the Build Scripts to move the data. -call %1BuildScripts\movedata.bat %1 %2 %3 %4 %5 %6 %7 %8 diff --git a/Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj b/Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj deleted file mode 100644 index 008f99815..000000000 --- a/Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj +++ /dev/null @@ -1,190 +0,0 @@ - - - - - DebugD3D12 - x64 - - - DebugVK - x64 - - - ReleaseD3D12 - x64 - - - ReleaseVK - x64 - - - - - - - - - - - - - - - - - - - - - - - - - True - - - {2C535635-E4C5-4098-A928-574F0E7CD5F9} - Win32Proj - FeatureDemo - 10.0.17763.0 - FalcorSharedObjects - - - - DynamicLibrary - true - v141 - Unicode - - - DynamicLibrary - true - v141 - Unicode - - - DynamicLibrary - false - v141 - true - Unicode - - - DynamicLibrary - false - v141 - true - Unicode - - - - - - - - - - - true - $(SolutionDir)Bin\$(PlatformShortName)\Debug\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\$(ProjectName)\ - - - true - $(SolutionDir)Bin\$(PlatformShortName)\Debug\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\$(ProjectName)\ - - - false - $(SolutionDir)Bin\$(PlatformShortName)\Release\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\$(ProjectName)\ - - - false - $(SolutionDir)Bin\$(PlatformShortName)\Release\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\$(ProjectName)\ - - - - - - Level3 - Disabled - BUILDING_SHARED_DLL;IMGUI_API=__declspec(dllexport);WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;GLM_FORCE_DEPTH_ZERO_TO_ONE;FALCOR_D3D12;%(PreprocessorDefinitions) - $(ProjectDir)..\Source\;$(ProjectDir)\..\Externals\GLM\;$(ProjectDir)..\;$(ProjectDir)..\Externals\pybind11\include\;$(ProjectDir)..\Externals\;$(ProjectDir)..\Externals\Python\Include\;$(ProjectDir)..\Externals\rapidjson\include\;$(ProjectDir)..\Externals\VulkanSDK\Include\;$(ProjectDir)..\Externals\NVAPI\ - - - Windows - true - $(ProjectDir)..\Externals\Python\libs\;$(ProjectDir)..\Externals\OpenVR\lib\win64\;$(ProjectDir)..\Externals\VulkanSDK\Lib\;$(ProjectDir)..\Externals\NVAPI\amd64\ - - - call $(ProjectDir)\..\BuildScripts\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) - - - - - - - Level3 - Disabled - BUILDING_SHARED_DLL;IMGUI_API=__declspec(dllexport);WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;GLM_FORCE_DEPTH_ZERO_TO_ONE;FALCOR_VK;%(PreprocessorDefinitions) - $(ProjectDir)..\Source\;$(ProjectDir)\..\Externals\GLM\;$(ProjectDir)..\;$(ProjectDir)..\Externals\pybind11\include\;$(ProjectDir)..\Externals\;$(ProjectDir)..\Externals\Python\Include\;$(ProjectDir)..\Externals\rapidjson\include\;$(ProjectDir)..\Externals\VulkanSDK\Include\;$(ProjectDir)..\Externals\NVAPI\ - - - Windows - true - $(ProjectDir)..\Externals\Python\libs\;$(ProjectDir)..\Externals\OpenVR\lib\win64\;$(ProjectDir)..\Externals\VulkanSDK\Lib\;$(ProjectDir)..\Externals\NVAPI\amd64\ - - - call $(ProjectDir)\..\BuildScripts\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) - - - - - Level3 - - - MaxSpeed - true - true - BUILDING_SHARED_DLL;IMGUI_API=__declspec(dllexport);WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NDEBUG;GLM_FORCE_DEPTH_ZERO_TO_ONE;FALCOR_VK;%(PreprocessorDefinitions) - $(ProjectDir)..\Source\;$(ProjectDir)\..\Externals\GLM\;$(ProjectDir)..\;$(ProjectDir)..\Externals\pybind11\include\;$(ProjectDir)..\Externals\;$(ProjectDir)..\Externals\Python\Include\;$(ProjectDir)..\Externals\rapidjson\include\;$(ProjectDir)..\Externals\VulkanSDK\Include\;$(ProjectDir)..\Externals\NVAPI\ - - - Windows - true - true - true - $(ProjectDir)..\Externals\Python\libs\;$(ProjectDir)..\Externals\OpenVR\lib\win64\;$(ProjectDir)..\Externals\VulkanSDK\Lib\;$(ProjectDir)..\Externals\NVAPI\amd64\ - - - call $(ProjectDir)\..\BuildScripts\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) - - - - - Level3 - - - MaxSpeed - true - true - BUILDING_SHARED_DLL;IMGUI_API=__declspec(dllexport);WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NDEBUG;GLM_FORCE_DEPTH_ZERO_TO_ONE;FALCOR_D3D12;%(PreprocessorDefinitions) - $(ProjectDir)..\Source\;$(ProjectDir)\..\Externals\GLM\;$(ProjectDir)..\;$(ProjectDir)..\Externals\pybind11\include\;$(ProjectDir)..\Externals\;$(ProjectDir)..\Externals\Python\Include\;$(ProjectDir)..\Externals\rapidjson\include\;$(ProjectDir)..\Externals\VulkanSDK\Include\;$(ProjectDir)..\Externals\NVAPI\ - - - Windows - true - true - true - $(ProjectDir)..\Externals\Python\libs\;$(ProjectDir)..\Externals\OpenVR\lib\win64\;$(ProjectDir)..\Externals\VulkanSDK\Lib\;$(ProjectDir)..\Externals\NVAPI\amd64\ - - - call $(ProjectDir)\..\BuildScripts\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) - - - - - - \ No newline at end of file diff --git a/Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj.filters b/Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj.filters deleted file mode 100644 index 4d9cb1157..000000000 --- a/Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj.filters +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui_addons - - - - - {52cd6688-eada-48ae-af64-3d8d29ed629d} - - - {12648101-552c-4a31-910a-b0ad1df000ba} - - - {8d6b13a1-6495-4f78-8fe6-3a4aa069d9d8} - - - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui - - - Externals\dear_imgui_addons - - - - - Externals\dear_imgui - - - Externals\dear_imgui - - - \ No newline at end of file diff --git a/Framework/Source/API/Buffer.cpp b/Framework/Source/API/Buffer.cpp deleted file mode 100644 index a7e158936..000000000 --- a/Framework/Source/API/Buffer.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/Buffer.h" -#include "API/Device.h" -#include - -namespace Falcor -{ - size_t getBufferDataAlignment(const Buffer* pBuffer); - void* mapBufferApi(const Buffer::ApiHandle& apiHandle, size_t size); - - Buffer::SharedPtr Buffer::create(size_t size, BindFlags usage, CpuAccess cpuAccess, const void* pInitData) - { - Buffer::SharedPtr pBuffer = SharedPtr(new Buffer(size, usage, cpuAccess)); - if (pBuffer->apiInit(pInitData != nullptr)) - { - if (pInitData) pBuffer->updateData(pInitData, 0, size); - return pBuffer; - } - else return nullptr; - } - - Buffer::~Buffer() - { - if (mDynamicData.pResourceHandle) - { - gpDevice->getResourceAllocator()->release(mDynamicData); - } - else - { - gpDevice->releaseResource(mApiHandle); - } - } - - void Buffer::updateData(const void* pData, size_t offset, size_t size) - { - if (mCpuAccess == CpuAccess::Write) - { - uint8_t* pDst = (uint8_t*)map(MapType::WriteDiscard) + offset; - std::memcpy(pDst, pData, size); - } - else - { - gpDevice->getRenderContext()->updateBuffer(this, pData, offset, size); - } - } - - void* Buffer::map(MapType type) - { - if (type == MapType::WriteDiscard) - { - if (mCpuAccess != CpuAccess::Write) - { - logError("Trying to map a buffer for write, but it wasn't created with the write permissions"); - return nullptr; - } - - // Allocate a new buffer - if (mDynamicData.pResourceHandle) - { - gpDevice->getResourceAllocator()->release(mDynamicData); - } - mDynamicData = gpDevice->getResourceAllocator()->allocate(mSize, getBufferDataAlignment(this)); - mApiHandle = mDynamicData.pResourceHandle; - invalidateViews(); - return mDynamicData.pData; - } - else - { - assert(type == MapType::Read); - - if (mBindFlags == BindFlags::None) - { - return mapBufferApi(mApiHandle, mSize); - } - else - { - logWarning("Buffer::map() performance warning - using staging resource which require us to flush the pipeline and wait for the GPU to finish its work"); - if (mpStagingResource == nullptr) - { - mpStagingResource = Buffer::create(mSize, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); - } - - // Copy the buffer and flush the pipeline - RenderContext* pContext = gpDevice->getRenderContext(); - pContext->copyResource(mpStagingResource.get(), this); - pContext->flush(true); - return mpStagingResource->map(MapType::Read); - } - } - } - - void CopyContext::updateBuffer(const Buffer* pBuffer, const void* pData, size_t offset, size_t numBytes) - { - if (numBytes == 0) - { - numBytes = pBuffer->getSize() - offset; - } - - if (pBuffer->adjustSizeOffsetParams(numBytes, offset) == false) - { - logWarning("CopyContext::updateBuffer() - size and offset are invalid. Nothing to update."); - return; - } - - mCommandsPending = true; - // Allocate a buffer on the upload heap - uint8_t* pInitData = (uint8_t*)pData + offset; - Buffer::SharedPtr pUploadBuffer = Buffer::create(numBytes, Buffer::BindFlags::None, Buffer::CpuAccess::Write, pInitData); - - copyBufferRegion(pBuffer, offset, pUploadBuffer.get(), 0, numBytes); - } -} diff --git a/Framework/Source/API/ComputeContext.cpp b/Framework/Source/API/ComputeContext.cpp deleted file mode 100644 index aac928949..000000000 --- a/Framework/Source/API/ComputeContext.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/ComputeContext.h" - -namespace Falcor -{ - ComputeContext::ComputeContext() - { - if (spDispatchCommandSig == nullptr) - { - initDispatchCommandSignature(); - } - } - - ComputeContext::~ComputeContext() = default; - CommandSignatureHandle ComputeContext::spDispatchCommandSig = nullptr; - - ComputeContext::SharedPtr ComputeContext::create(CommandQueueHandle queue) - { - SharedPtr pCtx = SharedPtr(new ComputeContext()); - pCtx->mpLowLevelData = LowLevelContextData::create(LowLevelContextData::CommandQueueType::Compute, queue); - if (pCtx->mpLowLevelData == nullptr) - { - return nullptr; - } - pCtx->bindDescriptorHeaps(); - return pCtx; - } - - void ComputeContext::pushComputeVars(const ComputeVars::SharedPtr& pVars) - { - mpComputeVarsStack.push(mpComputeVars); - setComputeVars(pVars); - } - - void ComputeContext::popComputeVars() - { - if (mpComputeVarsStack.empty()) - { - logWarning("Can't pop from the compute vars stack. The stack is empty"); - return; - } - - setComputeVars(mpComputeVarsStack.top()); - mpComputeVarsStack.pop(); - } - - void ComputeContext::pushComputeState(const ComputeState::SharedPtr& pState) - { - mpComputeStateStack.push(mpComputeState); - setComputeState(pState); - } - - void ComputeContext::popComputeState() - { - if (mpComputeStateStack.empty()) - { - logWarning("Can't pop from the compute state stack. The stack is empty"); - return; - } - - setComputeState(mpComputeStateStack.top()); - mpComputeStateStack.pop(); - } - - void ComputeContext::applyComputeVars() - { - if (mpComputeVars->apply(this, mBindComputeRootSig) == false) - { - logWarning("ComputeContext::prepareForDispatch() - applying ComputeVars failed, most likely because we ran out of descriptors. Flushing the GPU and retrying"); - flush(true); - if (!mpComputeVars->apply(this, mBindComputeRootSig)) - { - logError("ComputeVars::applyComputeVars() - applying ComputeVars failed, most likely because we ran out of descriptors", true); - assert(false); - } - } - } - - void ComputeContext::flush(bool wait) - { - CopyContext::flush(wait); - mBindComputeRootSig = true; - } -} diff --git a/Framework/Source/API/D3D12/D3D12ResourceViews.cpp b/Framework/Source/API/D3D12/D3D12ResourceViews.cpp deleted file mode 100644 index 4e81b2b5a..000000000 --- a/Framework/Source/API/D3D12/D3D12ResourceViews.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/ResourceViews.h" -#include "API/Resource.h" -#include "API/D3D12/D3DViews.h" -#include "API/Device.h" -#include "API/DescriptorSet.h" - -namespace Falcor -{ - template - ResourceView::~ResourceView() = default; - - ResourceWeakPtr getEmptyTexture() - { - return ResourceWeakPtr(); - } - - ShaderResourceView::SharedPtr ShaderResourceView::create(ResourceWeakPtr pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) - { - Resource::SharedConstPtr pSharedPtr = pResource.lock(); - if (!pSharedPtr && gNullSrv) - { - return gNullSrv; - } - - D3D12_SHADER_RESOURCE_VIEW_DESC desc; - Resource::ApiHandle resHandle = nullptr; - if(pSharedPtr) - { - initializeSrvDesc(pSharedPtr.get(), firstArraySlice, arraySize, mostDetailedMip, mipCount, desc); - resHandle = pSharedPtr->getApiHandle(); - } - else - { - desc = {}; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - } - SharedPtr pNewObj; - SharedPtr& pObj = pSharedPtr ? pNewObj : gNullSrv; - - DescriptorSet::Layout layout; - layout.addRange(DescriptorSet::Type::TextureSrv, 0, 1); - ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); - assert(handle); - gpDevice->getApiHandle()->CreateShaderResourceView(pSharedPtr ? pSharedPtr->getApiHandle() : nullptr, &desc, handle->getCpuHandle(0)); - - pObj = SharedPtr(new ShaderResourceView(pResource, handle, mostDetailedMip, mipCount, firstArraySlice, arraySize)); - return pObj; - } - - DepthStencilView::SharedPtr DepthStencilView::create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - Resource::SharedConstPtr pSharedPtr = pResource.lock(); - if (!pSharedPtr && gNullDsv) - { - return gNullDsv; - } - - D3D12_DEPTH_STENCIL_VIEW_DESC desc; - Resource::ApiHandle resHandle = nullptr; - if(pSharedPtr) - { - initializeDsvDesc(pSharedPtr.get(), mipLevel, firstArraySlice, arraySize, desc); - resHandle = pSharedPtr->getApiHandle(); - } - else - { - desc = {}; - desc.Format = DXGI_FORMAT_D16_UNORM; - desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; - } - SharedPtr pNewObj; - SharedPtr& pObj = pSharedPtr ? pNewObj : gNullDsv; - - DescriptorSet::Layout layout; - layout.addRange(DescriptorSet::Type::Dsv, 0, 1); - ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); - assert(handle); - gpDevice->getApiHandle()->CreateDepthStencilView(resHandle, &desc, handle->getCpuHandle(0)); - - pObj = SharedPtr(new DepthStencilView(pResource, handle, mipLevel, firstArraySlice, arraySize)); - return pObj; - } - - UnorderedAccessView::SharedPtr UnorderedAccessView::create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - Resource::SharedConstPtr pSharedPtr = pResource.lock(); - - if (!pSharedPtr && gNullUav) - { - return gNullUav; - } - - D3D12_UNORDERED_ACCESS_VIEW_DESC desc; - Resource::ApiHandle resHandle = nullptr; - Resource::ApiHandle counterHandle = nullptr; - - if(pSharedPtr != nullptr) - { - initializeUavDesc(pSharedPtr.get(), mipLevel, firstArraySlice, arraySize, desc); - resHandle = pSharedPtr->getApiHandle(); - - StructuredBuffer::SharedConstPtr pStructuredBuffer = std::dynamic_pointer_cast(pSharedPtr); - if (pStructuredBuffer != nullptr && pStructuredBuffer->hasUAVCounter()) - { - counterHandle = pStructuredBuffer->getUAVCounter()->getApiHandle(); - } - } - else - { - desc = {}; - desc.Format = DXGI_FORMAT_R32_UINT; - desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; - } - - SharedPtr pNewObj; - SharedPtr& pObj = pSharedPtr ? pNewObj : gNullUav; - - DescriptorSet::Layout layout; - layout.addRange(DescriptorSet::Type::TextureUav, 0, 1); - ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); - assert(handle); - gpDevice->getApiHandle()->CreateUnorderedAccessView(resHandle, counterHandle, &desc, handle->getCpuHandle(0)); - - pObj = SharedPtr(new UnorderedAccessView(pResource, handle, mipLevel, firstArraySlice, arraySize)); - - return pObj; - } - - RenderTargetView::~RenderTargetView() = default; - - RenderTargetView::SharedPtr RenderTargetView::create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - Resource::SharedConstPtr pSharedPtr = pResource.lock(); - - if (!pSharedPtr && gNullRtv) - { - return gNullRtv; - } - - D3D12_RENDER_TARGET_VIEW_DESC desc; - Resource::ApiHandle resHandle = nullptr; - if(pSharedPtr) - { - initializeRtvDesc(pSharedPtr.get(), mipLevel, firstArraySlice, arraySize, desc); - resHandle = pSharedPtr->getApiHandle(); - } - else - { - desc = {}; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;; - desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - } - - DescriptorSet::Layout layout; - layout.addRange(DescriptorSet::Type::Rtv, 0, 1); - ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); - assert(handle); - gpDevice->getApiHandle()->CreateRenderTargetView(resHandle, &desc, handle->getCpuHandle(0)); - - SharedPtr pNewObj; - SharedPtr& pObj = pSharedPtr ? pNewObj : gNullRtv; - - pObj = SharedPtr(new RenderTargetView(pResource, handle, mipLevel, firstArraySlice, arraySize)); - return pObj; - } - - ConstantBufferView::SharedPtr ConstantBufferView::create(ResourceWeakPtr pResource) - { - Resource::SharedConstPtr pSharedPtr = pResource.lock(); - - if (!pSharedPtr && gNullCbv) - { - return gNullCbv; - } - - D3D12_CONSTANT_BUFFER_VIEW_DESC desc; - Resource::ApiHandle resHandle = nullptr; - if (pSharedPtr) - { - ConstantBuffer::SharedConstPtr pBuffer = std::dynamic_pointer_cast(pSharedPtr); - desc.BufferLocation = pBuffer->getGpuAddress(); - desc.SizeInBytes = (uint32_t)pBuffer->getSize(); - resHandle = pSharedPtr->getApiHandle(); - } - else - { - desc = {}; - } - - DescriptorSet::Layout layout; - layout.addRange(DescriptorSet::Type::Cbv, 0, 1); - ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); - assert(handle); - gpDevice->getApiHandle()->CreateConstantBufferView(&desc, handle->getCpuHandle(0)); - - SharedPtr pNewObj; - SharedPtr& pObj = pSharedPtr ? pNewObj : gNullCbv; - - pObj = SharedPtr(new ConstantBufferView(pResource, handle)); - return pObj; - } -} - diff --git a/Framework/Source/API/D3D12/D3DViews.h b/Framework/Source/API/D3D12/D3DViews.h deleted file mode 100644 index de33a4ba1..000000000 --- a/Framework/Source/API/D3D12/D3DViews.h +++ /dev/null @@ -1,264 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/Resource.h" -#include "API/Buffer.h" -#include "API/Texture.h" -#include "API/TypedBuffer.h" - -namespace Falcor -{ - template - ViewType getViewDimension(Resource::Type type, bool isArray); - - template - void initializeSrvDesc(const Resource* pResource, uint32_t firstArraySlice, uint32_t arraySize, uint32_t mostDetailedMip, uint32_t mipCount, ViewDesc& desc) - { - const Texture* pTexture = dynamic_cast(pResource); - const Buffer* pBuffer = dynamic_cast(pResource); - - desc = {}; - - if (pBuffer) - { - const TypedBufferBase* pTypedBuffer = dynamic_cast(pResource); - const StructuredBuffer* pStructuredBuffer = dynamic_cast(pResource); - - desc.Buffer.FirstElement = 0; - if (pTypedBuffer) - { - desc.Format = getDxgiFormat(pTypedBuffer->getResourceFormat()); - desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; - desc.Buffer.NumElements = pTypedBuffer->getElementCount(); - } - else if (pStructuredBuffer) - { - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.Buffer.NumElements = (uint32_t)pStructuredBuffer->getElementCount(); - desc.Buffer.StructureByteStride = (uint32_t)pStructuredBuffer->getElementSize(); - } - else - { - desc.Format = DXGI_FORMAT_R32_TYPELESS; - desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; - desc.Buffer.NumElements = (uint32_t)pBuffer->getSize() / sizeof(float); - } - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - return; - } - - assert(pTexture); - - //If not depth, returns input format - ResourceFormat colorFormat = depthToColorFormat(pTexture->getFormat()); - desc.Format = getDxgiFormat(colorFormat); - - bool isTextureArray = pTexture->getArraySize() > 1; - desc.ViewDimension = getViewDimension(pResource->getType(), isTextureArray); - - switch(pResource->getType()) - { - case Resource::Type::Texture1D: - if (isTextureArray) - { - desc.Texture1DArray.MipLevels = mipCount; - desc.Texture1DArray.MostDetailedMip = mostDetailedMip; - desc.Texture1DArray.ArraySize = arraySize; - desc.Texture1DArray.FirstArraySlice = firstArraySlice; - } - else - { - desc.Texture1D.MipLevels = mipCount; - desc.Texture1D.MostDetailedMip = mostDetailedMip; - } - break; - case Resource::Type::Texture2D: - if(isTextureArray) - { - desc.Texture2DArray.MipLevels = mipCount; - desc.Texture2DArray.MostDetailedMip = mostDetailedMip; - desc.Texture2DArray.ArraySize = arraySize; - desc.Texture2DArray.FirstArraySlice = firstArraySlice; - } - else - { - desc.Texture2D.MipLevels = mipCount; - desc.Texture2D.MostDetailedMip = mostDetailedMip; - } - break; - case Resource::Type::Texture2DMultisample: - if(arraySize > 1) - { - desc.Texture2DMSArray.ArraySize = arraySize; - desc.Texture2DMSArray.FirstArraySlice = firstArraySlice; - } - break; - case Resource::Type::Texture3D: - assert(arraySize == 1); - desc.Texture3D.MipLevels = mipCount; - desc.Texture3D.MostDetailedMip = mostDetailedMip; - break; - case Resource::Type::TextureCube: - if(arraySize > 1) - { - desc.TextureCubeArray.First2DArrayFace = 0; - desc.TextureCubeArray.NumCubes = arraySize; - desc.TextureCubeArray.MipLevels = mipCount; - desc.TextureCubeArray.MostDetailedMip = mostDetailedMip; - } - else - { - desc.TextureCube.MipLevels = mipCount; - desc.TextureCube.MostDetailedMip = mostDetailedMip; - } - break; - default: - should_not_get_here(); - } -#ifdef FALCOR_D3D12 - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; -#endif - } - - template - inline void initializeDsvRtvUavDescCommon(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, DescType& desc) - { - const Texture* pTexture = dynamic_cast(pResource); - assert(pTexture); // Buffers should not get here - - desc = {}; - uint32_t arrayMultiplier = (pResource->getType() == Resource::Type::TextureCube) ? 6 : 1; - - if(arraySize == Resource::kMaxPossible) - { - arraySize = pTexture->getArraySize() - firstArraySlice; - } - - desc.ViewDimension = getViewDimension(pTexture->getType(), pTexture->getArraySize() > 1); - - switch(pResource->getType()) - { - case Resource::Type::Texture1D: - if(pTexture->getArraySize() > 1) - { - desc.Texture1DArray.ArraySize = arraySize; - desc.Texture1DArray.FirstArraySlice = firstArraySlice; - desc.Texture1DArray.MipSlice = mipLevel; - } - else - { - desc.Texture1D.MipSlice = mipLevel; - } - break; - case Resource::Type::Texture2D: - case Resource::Type::TextureCube: - if(pTexture->getArraySize() * arrayMultiplier > 1) - { - desc.Texture2DArray.ArraySize = arraySize * arrayMultiplier; - desc.Texture2DArray.FirstArraySlice = firstArraySlice * arrayMultiplier; - desc.Texture2DArray.MipSlice = mipLevel; - } - else - { - desc.Texture2D.MipSlice = mipLevel; - } - break; - default: - if(finalCall) - { - should_not_get_here(); - } - } - desc.Format = getDxgiFormat(pTexture->getFormat()); - } - - template - inline void initializeDsvRtvDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, DescType& desc) - { - initializeDsvRtvUavDescCommon(pResource, mipLevel, firstArraySlice, arraySize, desc); - - if(pResource->getType() == Resource::Type::Texture2DMultisample) - { - const Texture* pTexture = dynamic_cast(pResource); - if (pTexture->getArraySize() > 1) - { - desc.Texture2DMSArray.ArraySize = arraySize; - desc.Texture2DMSArray.FirstArraySlice = firstArraySlice; - } - } - } - - template - inline void initializeDsvDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, DescType& desc) - { - return initializeDsvRtvDesc(pResource, mipLevel, firstArraySlice, arraySize, desc); - } - - template - inline void initializeRtvDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, DescType& desc) - { - return initializeDsvRtvDesc(pResource, mipLevel, firstArraySlice, arraySize, desc); - } - - template - inline void initializeUavDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, DescType& desc) - { - if (pResource->getType() == Resource::Type::Buffer) - { - const Buffer* pBuffer = dynamic_cast(pResource); - const TypedBufferBase* pTypedBuffer = dynamic_cast(pBuffer); - const StructuredBuffer* pStructuredBuffer = dynamic_cast(pBuffer); - - desc = {}; - desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - - if (pTypedBuffer != nullptr) - { - desc.Format = getDxgiFormat(pTypedBuffer->getResourceFormat()); - desc.Buffer.NumElements = pTypedBuffer->getElementCount(); - } - else if (pStructuredBuffer != nullptr) - { - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.Buffer.NumElements = (uint32_t)pStructuredBuffer->getElementCount(); - desc.Buffer.StructureByteStride = (uint32_t)pStructuredBuffer->getElementSize(); - } - else - { - desc.Format = DXGI_FORMAT_R32_TYPELESS; - desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; - desc.Buffer.NumElements = (uint32_t)pBuffer->getSize() / sizeof(float); - } - } - else - { - initializeDsvRtvUavDescCommon(pResource, mipLevel, firstArraySlice, arraySize, desc); - } - } -} diff --git a/Framework/Source/API/FBO.cpp b/Framework/Source/API/FBO.cpp deleted file mode 100644 index 2af6c7868..000000000 --- a/Framework/Source/API/FBO.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/FBO.h" -#include "API/Texture.h" - -namespace Falcor -{ - std::unordered_set Fbo::sDescs; - - size_t Fbo::DescHash::operator()(const Fbo::Desc& d) const - { - size_t hash = 0; - std::hash u32hash; - std::hash bhash; - for (uint32_t i = 0; i < getMaxColorTargetCount(); i++) - { - uint32_t format = (uint32_t)d.getColorTargetFormat(i); - format <<= i; - hash |= u32hash(format) >> i; - hash |= bhash(d.isColorTargetUav(i)) << i; - } - - uint32_t format = (uint32_t)d.getDepthStencilFormat(); - hash |= u32hash(format); - hash |= bhash(d.isDepthStencilUav()); - hash |= u32hash(d.getSampleCount()); - - return hash; - } - - bool Fbo::Desc::operator==(const Fbo::Desc& other) const - { - if (mColorTargets.size() != other.mColorTargets.size()) return false; - - for (size_t i = 0; i < mColorTargets.size(); i++) - { - if (mColorTargets[i] != other.mColorTargets[i]) return false; - } - if (mDepthStencilTarget != other.mDepthStencilTarget) return false; - if (mSampleCount != other.mSampleCount) return false; - - return true; - } - - Fbo::Desc::Desc() - { - mColorTargets.resize(Fbo::getMaxColorTargetCount()); - } - - static bool checkAttachmentParams(const Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, bool isDepthAttachment) - { -#ifndef _DEBUG - return true; -#endif - if(pTexture == nullptr) - { - return true; - } - - if(mipLevel >= pTexture->getMipCount()) - { - logError("Error when attaching texture to FBO. Requested mip-level is out-of-bound."); - return false; - } - - if(arraySize != Fbo::kAttachEntireMipLevel) - { - if (arraySize == 0) - { - logError("Error when attaching texture to FBO. Requested to attach zero array slices"); - return false; - } - - if(pTexture->getType() == Texture::Type::Texture3D) - { - if(arraySize + firstArraySlice > pTexture->getDepth()) - { - logError("Error when attaching texture to FBO. Requested depth-index is out-of-bound."); - return false; - } - } - else - { - if(arraySize + firstArraySlice > pTexture->getArraySize()) - { - logError("Error when attaching texture to FBO. Requested array index is out-of-bound."); - return false; - } - } - } - - if(isDepthAttachment) - { - if(isDepthStencilFormat(pTexture->getFormat()) == false) - { - logError("Error when attaching texture to FBO. Attaching to depth-stencil target, but resource has color format."); - return false; - } - - if ((pTexture->getBindFlags() & Texture::BindFlags::DepthStencil) == Texture::BindFlags::None) - { - logError("Error when attaching texture to FBO. Attaching to depth-stencil target, the texture wasn't create with the DepthStencil bind flag"); - return false; - - } - } - else - { - if(isDepthStencilFormat(pTexture->getFormat())) - { - logError("Error when attaching texture to FBO. Attaching to color target, but resource has depth-stencil format."); - return false; - } - - if ((pTexture->getBindFlags() & Texture::BindFlags::RenderTarget) == Texture::BindFlags::None) - { - logError("Error when attaching texture to FBO. Attaching to color target, the texture wasn't create with the RenderTarget bind flag"); - return false; - - } - } - - return true; - } - - Fbo::SharedPtr Fbo::create() - { - return SharedPtr(new Fbo()); - } - - Fbo::SharedPtr Fbo::getDefault() - { - static Fbo::SharedPtr pDefault; - if(pDefault == nullptr) - { - pDefault = Fbo::SharedPtr(new Fbo()); - } - return pDefault; - } - - void Fbo::attachDepthStencilTarget(const Texture::SharedPtr& pDepthStencil, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - if(checkAttachmentParams(pDepthStencil.get(), mipLevel, firstArraySlice, arraySize, true)) - { - mpDesc = nullptr; - mDepthStencil.pTexture = pDepthStencil; - mDepthStencil.mipLevel = mipLevel; - mDepthStencil.firstArraySlice = firstArraySlice; - mDepthStencil.arraySize = arraySize; - bool allowUav = false; - if (pDepthStencil) - { - allowUav = ((pDepthStencil->getBindFlags() & Texture::BindFlags::UnorderedAccess) != Texture::BindFlags::None); - } - - mTempDesc.setDepthStencilTarget(pDepthStencil ? pDepthStencil->getFormat() : ResourceFormat::Unknown, allowUav); - applyDepthAttachment(); - } - } - - void Fbo::attachColorTarget(const Texture::SharedPtr& pTexture, uint32_t rtIndex, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - if(rtIndex >= mColorAttachments.size()) - { - logError("Error when attaching texture to FBO. Requested color index " + std::to_string(rtIndex) + ", but context only supports " + std::to_string(mColorAttachments.size()) + " targets"); - return; - } - - if(checkAttachmentParams(pTexture.get(), mipLevel, firstArraySlice, arraySize, false)) - { - mpDesc = nullptr; - mColorAttachments[rtIndex].pTexture = pTexture; - mColorAttachments[rtIndex].mipLevel = mipLevel; - mColorAttachments[rtIndex].firstArraySlice = firstArraySlice; - mColorAttachments[rtIndex].arraySize = arraySize; - bool allowUav = false; - if(pTexture) - { - allowUav = ((pTexture->getBindFlags() & Texture::BindFlags::UnorderedAccess) != Texture::BindFlags::None); - } - - mTempDesc.setColorTarget(rtIndex, pTexture ? pTexture->getFormat() : ResourceFormat::Unknown, allowUav); - applyColorAttachment(rtIndex); - } - } - - bool Fbo::verifyAttachment(const Attachment& attachment) const - { - const Texture* pTexture = attachment.pTexture.get(); - if(pTexture) - { - // Calculate size - if(mWidth == uint32_t(-1)) - { - // First attachment in the FBO - mTempDesc.setSampleCount(pTexture->getSampleCount()); - mIsLayered = (attachment.arraySize > 1); - } - - mWidth = min(mWidth, pTexture->getWidth(attachment.mipLevel)); - mHeight = min(mHeight, pTexture->getHeight(attachment.mipLevel)); - mDepth = min(mDepth, pTexture->getDepth(attachment.mipLevel)); - - { - if ( (pTexture->getSampleCount() > mTempDesc.getSampleCount()) && isDepthStencilFormat(pTexture->getFormat()) ) - { - // We're using target-independent raster (more depth samples than color samples). This is OK. - mTempDesc.setSampleCount(pTexture->getSampleCount()); - return true; - } - - if (mTempDesc.getSampleCount() != pTexture->getSampleCount()) - { - logError("Error when validating FBO. Different sample counts in attachments\n"); - return false; - } - - - if(mIsLayered != (attachment.arraySize > 1)) - { - logError("Error when validating FBO. Can't bind both layered and non-layered textures\n"); - return false; - } - } - } - return true; - } - - bool Fbo::calcAndValidateProperties() const - { - mWidth = (uint32_t)-1; - mHeight = (uint32_t)-1; - mDepth = (uint32_t)-1; - mTempDesc.setSampleCount(uint32_t(-1)); - mIsLayered = false; - - // Check color - for(const auto& attachment : mColorAttachments) - { - if(verifyAttachment(attachment) == false) - { - return false; - } - } - - // Check depth - if (verifyAttachment(mDepthStencil) == false) return false; - - // In case there are sample positions, make sure they are valid - if (mSamplePositions.size()) - { - uint32_t expectedCount = mSamplePositionsPixelCount * mTempDesc.getSampleCount(); - if (expectedCount != mSamplePositions.size()) - { - logError("Error when validating FBO. The sample-positions array-size has the wrong size.\n"); - return false; - } - } - - // Insert the attachment into the static array and initialize the address - mpDesc = &(*(sDescs.insert(mTempDesc).first)); - - return true; - } - - Texture::SharedPtr Fbo::getColorTexture(uint32_t index) const - { - if(index >= mColorAttachments.size()) - { - logError("CFbo::getColorTexture(): Index is out of range. Requested " + std::to_string(index) + " but only " + std::to_string(mColorAttachments.size()) + " color slots are available."); - return nullptr; - } - return mColorAttachments[index].pTexture; - } - - const Texture::SharedPtr& Fbo::getDepthStencilTexture() const - { - return mDepthStencil.pTexture; - } - - bool Fbo::checkStatus() const - { - if (mpDesc == nullptr) - { - if (calcAndValidateProperties() == false) return false; - initApiHandle(); - return true; - } - return true; - } - - void Fbo::setSamplePositions(uint32_t samplesPerPixel, uint32_t pixelCount, const SamplePosition positions[]) - { - if (positions) - { - mSamplePositions = std::vector(positions, positions + (samplesPerPixel * pixelCount)); - mSamplePositionsPixelCount = pixelCount; - } - else - { - mSamplePositionsPixelCount = 0; - mSamplePositions.clear(); - } - } -} \ No newline at end of file diff --git a/Framework/Source/API/Texture.cpp b/Framework/Source/API/Texture.cpp deleted file mode 100644 index 475965b8c..000000000 --- a/Framework/Source/API/Texture.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/Texture.h" -#include "API/Device.h" -#include "Utils/ThreadPool.h" - -namespace Falcor -{ - uint32_t Texture::tempDefaultUint = 0; - - Texture::BindFlags updateBindFlags(Texture::BindFlags flags, bool hasInitData, uint32_t mipLevels) - { - if ((mipLevels != Texture::kMaxPossible) || (hasInitData == false)) - { - return flags; - } - - flags |= Texture::BindFlags::RenderTarget; - return flags; - } - - Texture::SharedPtr Texture::createFromApiHandle(ApiHandle handle, Type type, uint32_t width, uint32_t height, uint32_t depth, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize, uint32_t mipLevels, State initState, BindFlags bindFlags) - { - switch (type) - { - case Resource::Type::Texture1D: - assert(height == 1 && depth == 1 && sampleCount == 1); - break; - case Resource::Type::Texture2D: - assert(depth == 1 && sampleCount == 1); - break; - case Resource::Type::Texture2DMultisample: - assert(depth == 1); - break; - case Resource::Type::Texture3D: - assert(sampleCount == 1); - break; - case Resource::Type::TextureCube: - assert(depth == 1 && sampleCount == 1); - break; - } - Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, depth, arraySize, mipLevels, sampleCount, format, type, bindFlags)); - pTexture->mApiHandle = handle; - - pTexture->mState.global = initState; - pTexture->mState.isGlobal = true; - return pTexture->mApiHandle ? pTexture : nullptr; - } - - Texture::SharedPtr Texture::create1D(uint32_t width, ResourceFormat format, uint32_t arraySize, uint32_t mipLevels, const void* pData, BindFlags bindFlags) - { - bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels); - Texture::SharedPtr pTexture = SharedPtr(new Texture(width, 1, 1, arraySize, mipLevels, 1, format, Type::Texture1D, bindFlags)); - pTexture->apinit(pData, (mipLevels == kMaxPossible)); - return pTexture->mApiHandle ? pTexture : nullptr; - } - - Texture::SharedPtr Texture::create2D(uint32_t width, uint32_t height, ResourceFormat format, uint32_t arraySize, uint32_t mipLevels, const void* pData, BindFlags bindFlags) - { - bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels); - Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, 1, arraySize, mipLevels, 1, format, Type::Texture2D, bindFlags)); - pTexture->apinit(pData, (mipLevels == kMaxPossible)); - return pTexture->mApiHandle ? pTexture : nullptr; - } - - Texture::SharedPtr Texture::create3D(uint32_t width, uint32_t height, uint32_t depth, ResourceFormat format, uint32_t mipLevels, const void* pData, BindFlags bindFlags, bool isSparse) - { - bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels); - Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, depth, 1, mipLevels, 1, format, Type::Texture3D, bindFlags)); - pTexture->apinit(pData, (mipLevels == kMaxPossible)); - return pTexture->mApiHandle ? pTexture : nullptr; - } - - // Texture Cube - Texture::SharedPtr Texture::createCube(uint32_t width, uint32_t height, ResourceFormat format, uint32_t arraySize, uint32_t mipLevels, const void* pData, BindFlags bindFlags) - { - bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels); - Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, 1, arraySize, mipLevels, 1, format, Type::TextureCube, bindFlags)); - pTexture->apinit(pData, (mipLevels == kMaxPossible)); - return pTexture->mApiHandle ? pTexture : nullptr; - } - - Texture::SharedPtr Texture::create2DMS(uint32_t width, uint32_t height, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize, BindFlags bindFlags) - { - Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, 1, arraySize, 1, sampleCount, format, Type::Texture2DMultisample, bindFlags)); - pTexture->apinit(nullptr, false); - return pTexture->mApiHandle ? pTexture : nullptr; - } - - Texture::Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, uint32_t sampleCount, ResourceFormat format, Type type, BindFlags bindFlags) - : Resource(type, bindFlags), mWidth(width), mHeight(height), mDepth(depth), mMipLevels(mipLevels), mSampleCount(sampleCount), mArraySize(arraySize), mFormat(format) - { - if(mMipLevels == kMaxPossible) - { - uint32_t dims = width | height | depth; - mMipLevels = bitScanReverse(dims) + 1; - } - mState.perSubresource.resize(mMipLevels * mArraySize, mState.global); - } - - void Texture::captureToFile(uint32_t mipLevel, uint32_t arraySlice, const std::string& filename, Bitmap::FileFormat format, Bitmap::ExportFlags exportFlags) const - { - uint32_t subresource = getSubresourceIndex(arraySlice, mipLevel); - std::vector textureData = gpDevice->getRenderContext()->readTextureSubresource(this, subresource); - - auto func = [=]() - { - Bitmap::saveImage(filename, getWidth(mipLevel), getHeight(mipLevel), format, exportFlags, getFormat(), true, (void*)textureData.data()); - }; - - static ThreadPool<16> sThreadPool; - sThreadPool.getAvailable() = std::thread(func); - } - - void Texture::uploadInitData(const void* pData, bool autoGenMips) - { - auto pRenderContext = gpDevice->getRenderContext(); - if (autoGenMips) - { - // Upload just the first mip-level - size_t arraySliceSize = mWidth * mHeight * getFormatBytesPerBlock(mFormat); - const uint8_t* pSrc = (uint8_t*)pData; - uint32_t numFaces = (mType == Texture::Type::TextureCube) ? 6 : 1; - for (uint32_t i = 0; i < mArraySize * numFaces; i++) - { - uint32_t subresource = getSubresourceIndex(i, 0); - pRenderContext->updateSubresourceData(this, subresource, pSrc); - pSrc += arraySliceSize; - } - } - else - { - pRenderContext->updateTextureData(this, pData); - } - - if (autoGenMips) - { - generateMips(gpDevice->getRenderContext()); - invalidateViews(); - } - } - - void Texture::generateMips(RenderContext* pContext) - { - if (mType != Type::Texture2D) - { - logWarning("Texture::generateMips() was only tested with Texture2Ds"); - } - // #OPTME: should blit support arrays? - for (uint32_t m = 0; m < mMipLevels - 1; m++) - { - for(uint32_t a = 0 ; a < mArraySize ; a++) - { - auto srv = getSRV(m, 1, a, 1); - auto rtv = getRTV(m + 1, a, 1); - pContext->blit(srv, rtv); - } - } - - if(mReleaseRtvsAfterGenMips) - { - // Releasing RTVs to free space on the heap. - // We only do it once to handle the case that generateMips() was called during load. - // If it was called more then once, the texture is probably dynamic and it's better to keep the RTVs around - mRtvs.clear(); - mReleaseRtvsAfterGenMips = false; - } - } -} diff --git a/Framework/Source/Effects/AmbientOcclusion/SSAO.cpp b/Framework/Source/Effects/AmbientOcclusion/SSAO.cpp deleted file mode 100644 index 2c27762c8..000000000 --- a/Framework/Source/Effects/AmbientOcclusion/SSAO.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "Framework.h" -#include "Effects/AmbientOcclusion/SSAO.h" -#include "Graphics/FboHelper.h" -#include "API/RenderContext.h" -#include "Graphics/Camera/Camera.h" -#include "glm/gtc/random.hpp" -#include "glm/gtc/packing.hpp" -#include "Utils/Math/FalcorMath.h" -#include "Graphics/Scene/Scene.h" - -namespace Falcor -{ - const char* SSAO::kDesc = "Screen-space ambient occlusion. Can be used with and without a normal-map"; - - const Gui::DropdownList SSAO::kDistributionDropdown = - { - { (uint32_t)SampleDistribution::Random, "Random" }, - { (uint32_t)SampleDistribution::UniformHammersley, "Uniform Hammersley" }, - { (uint32_t)SampleDistribution::CosineHammersley, "Cosine Hammersley" } - }; - - SSAO::SharedPtr SSAO::create(const uvec2& aoMapSize, uint32_t kernelSize, uint32_t blurSize, float blurSigma, const uvec2& noiseSize, SampleDistribution distribution) - { - return SharedPtr(new SSAO(aoMapSize, kernelSize, blurSize, blurSigma, noiseSize, distribution)); - } - - void SSAO::renderUI(Gui* pGui, const char* uiGroup) - { - if(!uiGroup || pGui->beginGroup(uiGroup)) - { - if (pGui->addDropdown("Kernel Distribution", kDistributionDropdown, mHemisphereDistribution)) - { - setKernel(mData.kernelSize, (SampleDistribution)mHemisphereDistribution); - } - - int32_t size = mData.kernelSize; - if (pGui->addIntVar("Kernel Size", size, 1, MAX_SAMPLES)) - { - setKernel((uint32_t)size, (SampleDistribution)mHemisphereDistribution); - } - - if (pGui->addFloatVar("Sample Radius", mData.radius, 0.001f, FLT_MAX)) - { - mDirty = true; - } - - pGui->addCheckBox("Apply Blur", mApplyBlur); - - if (mApplyBlur) - { - mpBlur->renderUI(pGui, "Blur Settings"); - } - - if (uiGroup) pGui->endGroup(); - } - } - - Texture::SharedPtr SSAO::generateAOMap(RenderContext* pContext, const Camera* pCamera, const Texture::SharedPtr& pDepthTexture, const Texture::SharedPtr& pNormalTexture) - { - upload(); - - // Update state/vars - mpSSAOState->setFbo(mpAOFbo); - ParameterBlock* pDefaultBlock = mpSSAOVars->getDefaultBlock().get(); - pDefaultBlock->setSampler(mBindLocations.noiseSampler, 0, mpNoiseSampler); - pDefaultBlock->setSampler(mBindLocations.textureSampler, 0, mpTextureSampler); - pDefaultBlock->setSrv(mBindLocations.depthTex, 0, pDepthTexture->getSRV()); - pDefaultBlock->setSrv(mBindLocations.noiseTex, 0, mpNoiseTexture->getSRV()); - pDefaultBlock->setSrv(mBindLocations.normalTex, 0, pNormalTexture ? pNormalTexture->getSRV() : nullptr); - - ConstantBuffer* pCB = pDefaultBlock->getConstantBuffer(mBindLocations.internalPerFrameCB, 0).get(); - if (pCB != nullptr) - { - pCamera->setIntoConstantBuffer(pCB, 0); - } - - // Generate AO - pContext->pushGraphicsState(mpSSAOState); - pContext->pushGraphicsVars(mpSSAOVars); - mpSSAOPass->execute(pContext); - pContext->popGraphicsVars(); - pContext->popGraphicsState(); - - // Blur - if (mApplyBlur) - { - mpBlur->execute(pContext, mpAOFbo->getColorTexture(0), mpAOFbo); - } - - return mpAOFbo->getColorTexture(0); - } - - SSAO::SSAO(const uvec2& aoMapSize, uint32_t kernelSize, uint32_t blurSize, float blurSigma, const uvec2& noiseSize, SampleDistribution distribution) : RenderPass("SSAO") - { - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, Falcor::ResourceFormat::R8Unorm); - mpAOFbo = FboHelper::create2D(aoMapSize.x, aoMapSize.y, fboDesc); - - initShader(); - - mpSSAOState = GraphicsState::create(); - - mpBlur = GaussianBlur::create(5, 2.0f); - - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap); - mpNoiseSampler = Sampler::create(samplerDesc); - - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpTextureSampler = Sampler::create(samplerDesc); - - setKernel(kernelSize, distribution); - setNoiseTexture(noiseSize.x, noiseSize.y); - - mComposeData.pApplySSAOPass = FullScreenPass::create("ApplyAO.ps.slang"); - mComposeData.pVars = GraphicsVars::create(mComposeData.pApplySSAOPass->getProgram()->getReflector()); - - Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mComposeData.pVars->setSampler("gSampler", Sampler::create(desc)); - mComposeData.pState = GraphicsState::create(); - mComposeData.pState->setFbo(Fbo::create()); - } - - void SSAO::upload() - { - if (mDirty) - { - ConstantBuffer* pCB = mpSSAOVars->getDefaultBlock()->getConstantBuffer(mBindLocations.ssaoCB, 0).get(); - if (pCB != nullptr) - { - pCB->setBlob(&mData, 0, sizeof(mData)); - } - - mDirty = false; - } - } - - void SSAO::initShader() - { - mpSSAOPass = FullScreenPass::create("Effects/SSAO.ps.slang"); - mpSSAOVars = GraphicsVars::create(mpSSAOPass->getProgram()->getReflector()); - - const ParameterBlockReflection* pReflector = mpSSAOPass->getProgram()->getReflector()->getDefaultParameterBlock().get(); - mBindLocations.internalPerFrameCB = pReflector->getResourceBinding("InternalPerFrameCB"); - mBindLocations.ssaoCB = pReflector->getResourceBinding("SSAOCB"); - mBindLocations.noiseSampler = pReflector->getResourceBinding("gNoiseSampler"); - mBindLocations.textureSampler = pReflector->getResourceBinding("gTextureSampler"); - mBindLocations.depthTex = pReflector->getResourceBinding("gDepthTex"); - mBindLocations.normalTex = pReflector->getResourceBinding("gNormalTex"); - mBindLocations.noiseTex = pReflector->getResourceBinding("gNoiseTex"); - } - - void SSAO::setKernel(uint32_t kernelSize, SampleDistribution distribution) - { - kernelSize = glm::clamp(kernelSize, (uint32_t)1, (uint32_t)MAX_SAMPLES); - mData.kernelSize = kernelSize; - - mHemisphereDistribution = (uint32_t)distribution; - - for (uint32_t i = 0; i < kernelSize; i++) - { - // Hemisphere in the Z+ direction - glm::vec3 p; - switch (distribution) - { - case SampleDistribution::Random: - p = glm::normalize(glm::linearRand(glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f))); - break; - - case SampleDistribution::UniformHammersley: - p = hammersleyUniform(i, kernelSize); - break; - - case SampleDistribution::CosineHammersley: - p = hammersleyCosine(i, kernelSize); - break; - } - - mData.sampleKernel[i] = glm::vec4(p, 0.0f); - - // Skew sample point distance on a curve so more cluster around the origin - float dist = (float)i / (float)kernelSize; - dist = glm::mix(0.1f, 1.0f, dist * dist); - mData.sampleKernel[i] *= dist; - } - - mDirty = true; - } - - void SSAO::setNoiseTexture(uint32_t width, uint32_t height) - { - std::vector data; - data.resize(width * height); - - for (uint32_t i = 0; i < width * height; i++) - { - // Random directions on the XY plane - glm::vec2 dir = glm::normalize(glm::linearRand(glm::vec2(-1), glm::vec2(1))) * 0.5f + 0.5f; - data[i] = glm::packUnorm4x8(glm::vec4(dir, 0.0f, 1.0f)); - } - - mpNoiseTexture = Texture::create2D(width, height, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, data.data()); - - mData.noiseScale = glm::vec2(mpAOFbo->getWidth(), mpAOFbo->getHeight()) / glm::vec2(width, height); - - mDirty = true; - } - - static const std::string kColorIn = "colorIn"; - static const std::string kColorOut = "colorOut"; - static const std::string kDepth = "depth"; - static const std::string kNormals = "normals"; - - RenderPassReflection SSAO::reflect() const - { - RenderPassReflection reflector; - reflector.addInput(kColorIn, "Color buffer"); - reflector.addOutput(kColorOut, "Color-buffer with AO applied to it"); - reflector.addInput(kDepth, "Depth-buffer"); - reflector.addInput(kNormals, "World space normals, [0, 1] range").flags(RenderPassReflection::Field::Flags::Optional); - return reflector; - } - - void SSAO::execute(RenderContext* pContext, const Camera* pCamera, const Texture::SharedPtr& pColorIn, const Texture::SharedPtr& pColorOut, const Texture::SharedPtr& pDepthTexture, const Texture::SharedPtr& pNormalTexture) - { - assert(pColorOut != pColorIn); - auto pAoMap = generateAOMap(pContext, mpScene->getActiveCamera().get(), pDepthTexture, pNormalTexture); - - mComposeData.pVars->setTexture("gColor", pColorIn); - mComposeData.pVars->setTexture("gAOMap", pAoMap); - auto pFbo = mComposeData.pState->getFbo(); - pFbo->attachColorTarget(pColorOut, 0); - mComposeData.pState->setFbo(pFbo); - - pContext->pushGraphicsState(mComposeData.pState); - pContext->pushGraphicsVars(mComposeData.pVars); - - mComposeData.pApplySSAOPass->execute(pContext); - - pContext->popGraphicsState(); - pContext->popGraphicsVars(); - } - - void SSAO::execute(RenderContext* pRenderContext, const RenderData* pData) - { - // Run the AO pass - auto pDepth = pData->getTexture(kDepth); - auto pNormals = pData->getTexture(kNormals); - auto pColorOut = pData->getTexture(kColorOut); - auto pColorIn = pData->getTexture(kColorIn); - execute(pRenderContext, mpScene->getActiveCamera().get(), pColorIn, pColorOut, pDepth, pNormals); - } -} diff --git a/Framework/Source/Effects/AmbientOcclusion/SSAO.h b/Framework/Source/Effects/AmbientOcclusion/SSAO.h deleted file mode 100644 index 742a990ae..000000000 --- a/Framework/Source/Effects/AmbientOcclusion/SSAO.h +++ /dev/null @@ -1,160 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once - -#include "API/FBO.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/FullScreenPass.h" -#include "Data/Effects/SSAOData.h" -#include "Effects/Utils/GaussianBlur.h" -#include "Utils/Gui.h" -#include "Experimental/RenderGraph/RenderPass.h" - -namespace Falcor -{ - class Gui; - class Camera; - class Scene; - - class SSAO : public RenderPass, public inherit_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - static const char* kDesc; - - enum class SampleDistribution - { - Random, - UniformHammersley, - CosineHammersley - }; - - /** Create an SSAO pass object sampling with a hemisphere kernel by default. - \param[in] aoMapSize Width and height of the AO map texture - \param[in] kernelSize Number of samples in the AO kernel - \param[in] blurSize Kernel size used for blurring the AO map - \param[in] blurSigma Sigma of the blur kernel - \param[in] noiseSize Width and height of the noise texture - \param[in] distribution Distribution of sample points when using a hemisphere kernel. - \return SSAO pass object. - */ - static SharedPtr create(const uvec2& aoMapSize, uint32_t kernelSize = 16, uint32_t blurSize = 5, float blurSigma = 2.0f, const uvec2& noiseSize = uvec2(16), SampleDistribution distribution = SampleDistribution::CosineHammersley); - static SharedPtr create(const Dictionary& dict) { return create(uvec2(1024)); } - - /** Render GUI for tweaking SSAO settings - */ - void renderUI(Gui* pGui, const char* uiGroup = "") override; - - /** Generate the AO map - \param[in] pContext Render context - \param[in] pCamera Camera used to render the scene - \param[in] pDepthTexture Scene depth buffer - \param[in] pNormalTexture Scene world-space normals buffer - \return AO map texture - */ - Texture::SharedPtr generateAOMap(RenderContext* pContext, const Camera* pCamera, const Texture::SharedPtr& pDepthTexture, const Texture::SharedPtr& pNormalTexture); - - /** Execute the SSAO pass and apply the result to the color target - pColorIn and pColorOut must be different - */ - void execute(RenderContext* pContext, const Camera* pCamera, const Texture::SharedPtr& pColorIn, const Texture::SharedPtr& pColorOut, const Texture::SharedPtr& pDepthTexture, const Texture::SharedPtr& pNormalTexture); - - /** Sets blur kernel size - */ - void setBlurKernelWidth(uint32_t width) { mpBlur->setKernelWidth(width); } - - /** Set blur sigma value - */ - void setBlurSigma(float sigma) { mpBlur->setSigma(sigma); } - - /** Recreate sampling kernel - \param[in] kernelSize Number of samples - \param[in] distribution Distribution of sample points within a hemisphere kernel. Parameter is ignored for sphere kernel generation, but is saved for use in future hemisphere kernels. - */ - void setKernel(uint32_t kernelSize, SampleDistribution distribution = SampleDistribution::Random); - - /** Recreate noise texture - \param[in] width Noise texture width - \param[in] height Noise texture height - */ - void setNoiseTexture(uint32_t width, uint32_t height); - - // Render-pass functions - RenderPassReflection reflect() const override; - void execute(RenderContext* pRenderContext, const RenderData* pData) override; - void setScene(const std::shared_ptr& pScene) override { mpScene = pScene; } - std::string getDesc() override { return kDesc; } - private: - - SSAO(const uvec2& aoMapSize, uint32_t kernelSize, uint32_t blurSize, float blurSigma, const uvec2& noiseSize, SampleDistribution distribution); - - void upload(); - void initShader(); - - SSAOData mData; - bool mDirty = false; - - Fbo::SharedPtr mpAOFbo; - GraphicsState::SharedPtr mpSSAOState; - Sampler::SharedPtr mpNoiseSampler; - Texture::SharedPtr mpNoiseTexture; - - Sampler::SharedPtr mpTextureSampler; - - struct - { - ProgramReflection::BindLocation internalPerFrameCB; - ProgramReflection::BindLocation ssaoCB; - ProgramReflection::BindLocation noiseSampler; - ProgramReflection::BindLocation textureSampler; - ProgramReflection::BindLocation depthTex; - ProgramReflection::BindLocation normalTex; - ProgramReflection::BindLocation noiseTex; - } mBindLocations; - - uint32_t mHemisphereDistribution = (uint32_t)SampleDistribution::CosineHammersley; - - static const Gui::DropdownList kKernelDropdown; - static const Gui::DropdownList kDistributionDropdown; - - FullScreenPass::UniquePtr mpSSAOPass; - GraphicsVars::SharedPtr mpSSAOVars; - - bool mApplyBlur = true; - GaussianBlur::UniquePtr mpBlur; - std::shared_ptr mpScene; - - struct - { - FullScreenPass::UniquePtr pApplySSAOPass; - GraphicsVars::SharedPtr pVars; - GraphicsState::SharedPtr pState; - } mComposeData; - }; - -} \ No newline at end of file diff --git a/Framework/Source/Effects/FXAA/FXAA.cpp b/Framework/Source/Effects/FXAA/FXAA.cpp deleted file mode 100644 index 4aa038e49..000000000 --- a/Framework/Source/Effects/FXAA/FXAA.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. -***************************************************************************/ -#include "Framework.h" -#include "FXAA.h" -#include "Utils/Gui.h" -#include "API/RenderContext.h" - -namespace Falcor -{ - const char* FXAA::kDesc = "Fast Approximate Anti-Aliasing"; - - static const char* kShaderFilename = "Effects/FXAA.slang"; - - FXAA::~FXAA() = default; - - FXAA::FXAA() : RenderPass("FXAA") - { - mpPass = FullScreenPass::create(kShaderFilename); - mpGraphicsVars = GraphicsVars::create(mpPass->getProgram()->getReflector()); - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - mpLinearSampler = Sampler::create(samplerDesc); - mpGraphicsVars->setSampler("gSampler", mpLinearSampler); - } - - FXAA::SharedPtr FXAA::create(const Dictionary& dict) - { - try - { - FXAA* pFxaa = new FXAA(); - return FXAA::SharedPtr(pFxaa); - } - catch (const std::exception&) - { - return nullptr; - } - } - - void FXAA::renderUI(Gui* pGui, const char* uiGroup) - { - if (!uiGroup || pGui->beginGroup(uiGroup)) - { - pGui->addFloatVar("Sub-Pixel Quality", mQualitySubPix, 0, 1); - pGui->addFloatVar("Edge Threshold", mQualityEdgeThreshold, 0, 1); - pGui->addFloatVar("Edge Threshold Min", mQualityEdgeThresholdMin, 0, 1); - pGui->addCheckBox("Early out", mEarlyOut); - if (uiGroup) pGui->endGroup(); - } - } - - void FXAA::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pSrcTex, const Fbo::SharedPtr& pDstFbo) - { - mpGraphicsVars->setTexture("gSrc", pSrcTex); - vec2 rcpFrame = 1.0f / vec2(pSrcTex->getWidth(), pSrcTex->getHeight()); - auto pCB = mpGraphicsVars->getDefaultBlock()["PerFrameCB"]; - pCB["rcpTexDim"] = rcpFrame; - pCB["qualitySubPix"] = mQualitySubPix; - pCB["qualityEdgeThreshold"] = mQualityEdgeThreshold; - pCB["qualityEdgeThresholdMin"] = mQualityEdgeThresholdMin; - pCB["earlyOut"] = mEarlyOut; - - mpGraphicsVars->setSampler("gSampler", mpLinearSampler); - - pRenderContext->pushGraphicsVars(mpGraphicsVars); - pRenderContext->getGraphicsState()->pushFbo(pDstFbo); - - mpPass->execute(pRenderContext); - - pRenderContext->getGraphicsState()->popFbo(); - pRenderContext->popGraphicsVars(); - } - - static const std::string kSrc = "src"; - static const std::string kDst = "dst"; - - RenderPassReflection FXAA::reflect() const - { - RenderPassReflection reflector; - reflector.addInput(kSrc, "Source color-buffer"); - reflector.addOutput(kDst, "Destination color-buffer"); - return reflector; - } - - void FXAA::execute(RenderContext* pContext, const RenderData* pData) - { - auto pSrc = pData->getTexture(kSrc); - auto pDst = pData->getTexture(kDst); - - Fbo::SharedPtr pFbo = Fbo::create(); - pFbo->attachColorTarget(pDst, 0); - execute(pContext, pSrc, pFbo); - } -} \ No newline at end of file diff --git a/Framework/Source/Effects/NormalMap/LeanMap.cpp b/Framework/Source/Effects/NormalMap/LeanMap.cpp deleted file mode 100644 index cccb70ef2..000000000 --- a/Framework/Source/Effects/NormalMap/LeanMap.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "LeanMap.h" -#include "Graphics/Material/Material.h" -#include "Graphics/Scene/Scene.h" -#include "API/Device.h" - -namespace Falcor -{ - Texture::SharedPtr LeanMap::createFromNormalMap(const Falcor::Texture* pNormalMap) - { - uint32_t texW = pNormalMap->getWidth(); - uint32_t texH = pNormalMap->getHeight(); - - std::vector leanData; - - leanData.resize(texW * texH); - - auto normalMapData = gpDevice->getRenderContext()->readTextureSubresource(pNormalMap, 0); - - const float oneBy255 = 1.0f / 255.0f; - for(auto y = 0u; y < texH; y++) - { - for(auto x = 0u; x < texW; x++) - { - auto texIdx = (x + y * texW); - vec3 tn; - - switch(pNormalMap->getFormat()) - { - case ResourceFormat::RGBA8Unorm: - { - tn.x = clamp(oneBy255 * (float)(normalMapData[texIdx * 4 + 0]), 0.0f, 1.0f); - tn.y = clamp(oneBy255 * (float)(normalMapData[texIdx * 4 + 1]), 0.0f, 1.0f); - tn.z = clamp(oneBy255 * (float)(normalMapData[texIdx * 4 + 2]), 0.0f, 1.0f); - } break; - case ResourceFormat::BGRA8Unorm: - case ResourceFormat::BGRX8Unorm: - { - tn.z = clamp(oneBy255 * (float)(normalMapData[texIdx * 4 + 0]), 0.0f, 1.0f); - tn.y = clamp(oneBy255 * (float)(normalMapData[texIdx * 4 + 1]), 0.0f, 1.0f); - tn.x = clamp(oneBy255 * (float)(normalMapData[texIdx * 4 + 2]), 0.0f, 1.0f); - } break; - case ResourceFormat::RGBA8UnormSrgb: - { - tn.x = clamp(sRGBToLinear(oneBy255 * (float)(normalMapData[texIdx * 4 + 0])), 0.0f, 1.0f); - tn.y = clamp(sRGBToLinear(oneBy255 * (float)(normalMapData[texIdx * 4 + 1])), 0.0f, 1.0f); - tn.z = clamp(sRGBToLinear(oneBy255 * (float)(normalMapData[texIdx * 4 + 2])), 0.0f, 1.0f); - } break; - case ResourceFormat::BGRA8UnormSrgb: - { - tn.z = clamp(sRGBToLinear(oneBy255 * (float)(normalMapData[texIdx * 4 + 0])), 0.0f, 1.0f); - tn.y = clamp(sRGBToLinear(oneBy255 * (float)(normalMapData[texIdx * 4 + 1])), 0.0f, 1.0f); - tn.x = clamp(sRGBToLinear(oneBy255 * (float)(normalMapData[texIdx * 4 + 2])), 0.0f, 1.0f); - } break; - default: - logError("Can't generate LEAN map. Unsupported normal map format."); - return nullptr; - }; - - // Unpack - static const float epsilon = 1e-3f; - vec3 n = tn * 2.f - vec3(1.f); - // And normalize the normal - n.z = max(n.z, epsilon); - n = normalize(n); - - // Write out the first moment (mean) in slope space - vec2 b = vec2(n.x, n.y) / max(n.z, epsilon); - vec2 m = b*b; - leanData[texIdx] = vec4(b.x*0.5f + 0.5f, b.y*0.5f + 0.5f, m.x, m.y); - } - } - - Texture::SharedPtr pTex = Texture::create2D(texW, texH, ResourceFormat::RGBA32Float, 1, Texture::kMaxPossible, leanData.data()); - return pTex; - } - - bool LeanMap::createLeanMap(const Material* pMaterial) - { - uint32_t materialID = pMaterial->getId(); - - if(mpLeanMaps.find(materialID) != mpLeanMaps.end()) - { - logError("Error when creating SceneLeanMaps. Scene material IDs should be unique for scene materials."); - return false; - } - - const Texture* pNormalMap = pMaterial->getNormalMap().get(); - if(pNormalMap) - { - mpLeanMaps[materialID] = createFromNormalMap(pNormalMap); - mShaderArraySize = max(materialID + 1, mShaderArraySize); - } - return true; - } - - LeanMap::UniquePtr LeanMap::create(const Scene* pScene) - { - UniquePtr pLeanMaps = UniquePtr(new LeanMap); - - // Initialize model materials - for(uint32_t model = 0; model < pScene->getModelCount(); model++) - { - const Model* pModel = pScene->getModel(model).get(); - for(uint32_t meshID = 0; meshID < pModel->getMeshCount(); meshID++) - { - const Material* pMaterial = pModel->getMesh(meshID)->getMaterial().get(); - if(pLeanMaps->createLeanMap(pMaterial) == false) - { - return nullptr; - } - } - } - - if(pLeanMaps->mpLeanMaps.size() == 0) - { - logWarning("Trying to create SceneLeanMaps for a scene without materials."); - } - - return pLeanMaps; - } - - void LeanMap::setIntoProgramVars(ProgramVars* pVars, const Sampler::SharedPtr& pSampler) const - { - for (const auto& pMap : mpLeanMaps) - { - std::string name("gLeanMaps"); - if(mpLeanMaps.size() > 1) - { - uint32_t id = pMap.first; - name += "[" + std::to_string(id) + "]"; - } - Texture::SharedPtr pTex = pMap.second; - pVars->setTexture(name, pTex); - } - - pVars->setSampler("gLeanMapSampler", pSampler); - } -} \ No newline at end of file diff --git a/Framework/Source/Effects/SkyBox/SkyBox.cpp b/Framework/Source/Effects/SkyBox/SkyBox.cpp deleted file mode 100644 index 49a5ccaa0..000000000 --- a/Framework/Source/Effects/SkyBox/SkyBox.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "SkyBox.h" -#include "glm/gtx/transform.hpp" -#include "Graphics/TextureHelper.h" -#include "Graphics/Camera/Camera.h" -#include "Graphics/Model/ModelRenderer.h" -#include "Graphics/Scene/Scene.h" - -namespace Falcor -{ - const char* SkyBox::kDesc = "Render an environment-map. The map can be provided by the user or taken from a scene"; - - // Dictionary keys - static const std::string& kSkyboxFile = "file"; - - SkyBox::SkyBox() : RenderPass("SkyBox") {} - - SkyBox::SharedPtr SkyBox::create(const Texture::SharedPtr& pTexture, const Sampler::SharedPtr& pSampler, bool renderStereo) - { - SharedPtr pSkyBox = SharedPtr(new SkyBox()); - if(pSkyBox->createResources(pTexture, pSampler, renderStereo) == false) - { - return nullptr; - } - return pSkyBox; - } - - SkyBox::SharedPtr SkyBox::create(const std::string& textureName, bool loadAsSrgb, Sampler::SharedPtr pSampler, bool renderStereo) - { - Texture::SharedPtr pTexture; - if (textureName.size()) - { - pTexture = createTextureFromFile(textureName, false, loadAsSrgb); - if (pTexture == nullptr) - { - return nullptr; - } - } - - SharedPtr pSkyBox = SharedPtr(new SkyBox()); - if (pSkyBox->createResources(pTexture, pSampler, renderStereo) == false) return nullptr; - return pSkyBox; - } - - SkyBox::SharedPtr SkyBox::create(const Dictionary& dict) - { - std::string filename; - Dictionary::Value v = dict[kSkyboxFile]; - if (dict.keyExists(kSkyboxFile)) filename = (dict[kSkyboxFile]).operator std::string(); - - return create(filename); - } - - Dictionary SkyBox::getScriptingDictionary() const - { - return Dictionary(); - } - - void SkyBox::setTexture(const Texture::SharedPtr& pTexture) - { - mpTexture = pTexture; - if (mpTexture) - { - assert(mpTexture->getType() == Texture::Type::TextureCube || mpTexture->getType() == Texture::Type::Texture2D); - (mpTexture->getType() == Texture::Type::Texture2D) ? mpProgram->addDefine("_SPHERICAL_MAP") : mpProgram->removeDefine("_SPHERICAL_MAP"); - } - mpVars->setTexture("gTexture", mpTexture); - } - - bool SkyBox::createResources(const Texture::SharedPtr& pTexture, const Sampler::SharedPtr& pSampler, bool renderStereo) - { - mpCubeModel = Model::createFromFile("Effects/cube.obj"); - if(mpCubeModel == nullptr) - { - logError("Failed to load cube model for SkyBox"); - return false; - } - - // Create the program - Program::DefineList defines; - if(renderStereo) - { - defines.add("_SINGLE_PASS_STEREO"); - } - - mpProgram = GraphicsProgram::createFromFile("Effects/SkyBox.slang", "vs", "ps", defines); - mpVars = GraphicsVars::create(mpProgram->getReflector()); - - const ParameterBlockReflection* pDefaultBlockReflection = mpProgram->getReflector()->getDefaultParameterBlock().get(); - mBindLocations.perFrameCB = pDefaultBlockReflection->getResourceBinding("PerFrameCB"); - mBindLocations.texture = pDefaultBlockReflection->getResourceBinding("gTexture"); - mBindLocations.sampler = pDefaultBlockReflection->getResourceBinding("gSampler"); - - ParameterBlock* pDefaultBlock = mpVars->getDefaultBlock().get(); - ConstantBuffer* pCB = pDefaultBlock->getConstantBuffer(mBindLocations.perFrameCB, 0).get(); - mScaleOffset = pCB->getVariableOffset("gScale"); - mMatOffset = pCB->getVariableOffset("gWorld"); - - // Create state - mpState = GraphicsState::create(); - BlendState::Desc blendDesc; - for(uint32_t i = 1 ; i < Fbo::getMaxColorTargetCount() ; i++) - { - blendDesc.setRenderTargetWriteMask(i, false, false, false, false); - } - blendDesc.setIndependentBlend(true); - mpState->setBlendState(BlendState::create(blendDesc)); - - // Create the rasterizer state - RasterizerState::Desc rastDesc; - rastDesc.setCullMode(RasterizerState::CullMode::Front).setDepthClamp(true); - mpState->setRasterizerState(RasterizerState::create(rastDesc)); - - DepthStencilState::Desc dsDesc; - dsDesc.setDepthWriteMask(false).setDepthFunc(DepthStencilState::Func::LessEqual).setDepthTest(true); - mpState->setDepthStencilState(DepthStencilState::create(dsDesc)); - mpState->setProgram(mpProgram); - - setTexture(pTexture); - setSampler(pSampler); - - return true; - } - - Sampler::SharedPtr SkyBox::getSampler() const - { - return mpVars->getDefaultBlock()->getSampler(mBindLocations.sampler, 0); - } - - Texture::SharedPtr SkyBox::getTexture() const - { - return mpTexture; - } - - void SkyBox::setSampler(Sampler::SharedPtr pSampler) - { - mpVars->getDefaultBlock()->setSampler(mBindLocations.sampler, 0, pSampler); - } - - void SkyBox::render(RenderContext* pRenderCtx, Camera* pCamera, const Fbo::SharedPtr& pTarget) - { - glm::mat4 world = glm::translate(pCamera->getPosition()); - ConstantBuffer* pCB = mpVars->getDefaultBlock()->getConstantBuffer(mBindLocations.perFrameCB, 0).get(); - pCB->setVariable(mMatOffset, world); - pCB->setVariable(mScaleOffset, mScale); - - mpState->setFbo(pTarget ? pTarget : pRenderCtx->getGraphicsState()->getFbo()); - pRenderCtx->pushGraphicsVars(mpVars); - pRenderCtx->pushGraphicsState(mpState); - - ModelRenderer::render(pRenderCtx, mpCubeModel, pCamera, false); - - pRenderCtx->popGraphicsVars(); - pRenderCtx->popGraphicsState(); - } - - static const std::string kTarget = "target"; - static const std::string kDepth = "depth"; - - RenderPassReflection SkyBox::reflect() const - { - RenderPassReflection reflector; - - reflector.addOutput(kTarget, "Color buffer").format(ResourceFormat::RGBA32Float); - reflector.addInputOutput(kDepth, "Depth-buffer. Should be pre-initialized or cleared before calling the pass").bindFlags(Resource::BindFlags::DepthStencil); - return reflector; - } - - void SkyBox::execute(RenderContext* pRenderContext, const RenderData* pData) - { - DepthStencilState::Desc dsDesc; - dsDesc.setDepthFunc(DepthStencilState::Func::Always); - auto pDS = DepthStencilState::create(dsDesc); - - if (!mpFbo) mpFbo = Fbo::create(); - mpFbo->attachColorTarget(pData->getTexture(kTarget), 0); - mpFbo->attachDepthStencilTarget(pData->getTexture(kDepth)); - - pRenderContext->clearRtv(mpFbo->getRenderTargetView(0).get(), vec4(0)); - render(pRenderContext, mpScene->getActiveCamera().get(), mpFbo); - } - - void SkyBox::setScene(const std::shared_ptr& pScene) -{ - mpScene = pScene; - if (mpScene && mpScene->getEnvironmentMap()) setTexture(mpScene->getEnvironmentMap()); - } -} \ No newline at end of file diff --git a/Framework/Source/Effects/SkyBox/SkyBox.h b/Framework/Source/Effects/SkyBox/SkyBox.h deleted file mode 100644 index 45410f36f..000000000 --- a/Framework/Source/Effects/SkyBox/SkyBox.h +++ /dev/null @@ -1,140 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "API/Sampler.h" -#include "API/Texture.h" -#include "Graphics/Model/Model.h" -#include "Graphics/Program/Program.h" -#include "API/ConstantBuffer.h" -#include "API/DepthStencilState.h" -#include "API/RasterizerState.h" -#include "API/BlendState.h" -#include "Experimental/RenderGraph/RenderPass.h" - -namespace Falcor -{ - class RenderContext; - - class SkyBox : public RenderPass, inherit_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - static const char* kDesc; - - /** Create a sky box using an existing texture - \param[in] pTexture Sky box texture - \param[in] pSampler Sampler to use when rendering this sky box - \param[in] renderStereo Whether to render in stereo mode using Single Pass Stereo - */ - static SharedPtr create(const Texture::SharedPtr& pTexture, const Sampler::SharedPtr& pSampler = nullptr, bool renderStereo = false); - static SharedPtr create(const Dictionary& dict); - - /** Load a texture and create a sky box using it. - \param[in] textureFile Filename of texture. Can include a full or relative path from a data directory - \param[in] loadAsSrgb Whether to load the texture into an sRGB format - \param[in] pSampler Sampler to use when rendering this sky box - \param[in] renderStereo Whether to render in stereo mode using Single Pass Stereo - */ - static SharedPtr create(const std::string& textureFile, bool loadAsSrgb = true, Sampler::SharedPtr pSampler = nullptr, bool renderStereo = false); - - /** Render the sky box. - \param[in] pRenderCtx Render context - \param[in] pCamera Camera to use when rendering - \param[in, optional] The target FBO. If this is nullptr, the currently bound FBO will be used - */ - void render(RenderContext* pRenderCtx, Camera* pCamera, const Fbo::SharedPtr& pTarget = nullptr); - - /* Save out data for sky box into serializer - \param[in] pRenderPass SkyBoxPass to serialize data from - */ - virtual Dictionary getScriptingDictionary() const override; - - /** Set the sampler used to render the sky box. - */ - void setSampler(Sampler::SharedPtr pSampler); - - /** Get the sky box texture. - */ - Texture::SharedPtr getTexture() const; - - /** Set a new texture - */ - void setTexture(const Texture::SharedPtr& pTexture); - - /** Get the sampler used to render the sky box. - */ - Sampler::SharedPtr getSampler() const; - - void setScale(float scale) { mScale = scale; } - float getScale() const { return mScale; } - - /** Called once before compilation. Describes I/O requirements of the pass. - The requirements can't change after the graph is compiled. If the IO requests are dynamic, you'll need to trigger compilation of the render-graph yourself. - */ - virtual RenderPassReflection reflect() const override; - - /** Executes the pass. - */ - virtual void execute(RenderContext* pRenderContext, const RenderData* pData) override; - - /** Set a scene. The pass will use the environment from the scene if one exists - */ - virtual void setScene(const std::shared_ptr& pScene) override; - - /** Get a description of the scene - */ - std::string getDesc() override { return kDesc; } - private: - SkyBox(); - bool createResources(const Texture::SharedPtr& pTexture, const Sampler::SharedPtr& pSampler, bool renderStereo); - - size_t mMatOffset; - size_t mScaleOffset; - - float mScale = 1; - Model::SharedPtr mpCubeModel; - Texture::SharedPtr mpTexture; - - GraphicsProgram::SharedPtr mpProgram; - GraphicsVars::SharedPtr mpVars; - GraphicsState::SharedPtr mpState; - Fbo::SharedPtr mpFbo; - std::shared_ptr mpScene; - Sampler::SharedPtr mpSampler; - bool mLoadSrgb = true; - bool mRenderStereo = false; - - struct - { - ProgramReflection::BindLocation perFrameCB; - ProgramReflection::BindLocation sampler; - ProgramReflection::BindLocation texture; - } mBindLocations; - }; -} \ No newline at end of file diff --git a/Framework/Source/Effects/TAA/TAA.cpp b/Framework/Source/Effects/TAA/TAA.cpp deleted file mode 100644 index 59ab66690..000000000 --- a/Framework/Source/Effects/TAA/TAA.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. -***************************************************************************/ -#include "Framework.h" -#include "TAA.h" -#include "Utils/Gui.h" -#include "API/RenderContext.h" - -namespace Falcor -{ - const char* TemporalAA::kDesc = "Temporal Anti-Aliasing"; - - static const char* kShaderFilename = "Effects/TAA.ps.slang"; - - TemporalAA::~TemporalAA() = default; - - TemporalAA::TemporalAA() : RenderPass("TemporalAA") - { - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpLinearSampler = Sampler::create(samplerDesc); - createProgram(); - } - - TemporalAA::SharedPtr TemporalAA::create(const Dictionary& dict) - { - TemporalAA* pTaa = new TemporalAA(); - return TemporalAA::SharedPtr(pTaa); - } - - void TemporalAA::renderUI(Gui* pGui, const char* uiGroup) - { - if(!uiGroup || pGui->beginGroup(uiGroup)) - { - pGui->addFloatVar("Alpha", mControls.alpha, 0, 1.0f); - pGui->addFloatVar("Color-Box Sigma", mControls.colorBoxSigma, 0, 15); - - if (uiGroup) pGui->endGroup(); - } - } - - void TemporalAA::createProgram() - { - mpProgram = FullScreenPass::create(kShaderFilename); - ProgramReflection::SharedConstPtr pReflector = mpProgram->getProgram()->getReflector(); - mpProgVars = GraphicsVars::create(pReflector); - mpCB = mpProgVars->getConstantBuffer("PerFrameCB"); - - // Initialize the CB offsets - mVarLocations.alpha = mpCB->getVariableOffset("gAlpha"); - mVarLocations.colorBoxSigma = mpCB->getVariableOffset("gColorBoxSigma"); - - // Get the textures data - const auto& pDefaultBlock = pReflector->getDefaultParameterBlock(); - mVarLocations.colorTex = pDefaultBlock->getResourceBinding("gTexColor"); - mVarLocations.prevColorTex = pDefaultBlock->getResourceBinding("gTexPrevColor"); - mVarLocations.motionVecTex = pDefaultBlock->getResourceBinding("gTexMotionVec"); - mVarLocations.sampler = pDefaultBlock->getResourceBinding("gSampler"); - } - - void TemporalAA::setVarsData(const Texture::SharedPtr & pCurColor, const Texture::SharedPtr & pPrevColor, const Texture::SharedPtr & pMotionVec) - { - // Make sure the dimensions match - assert((pCurColor->getWidth() == pPrevColor->getWidth()) && (pCurColor->getWidth() == pMotionVec->getWidth())); - assert((pCurColor->getHeight() == pPrevColor->getHeight()) && (pCurColor->getHeight() == pMotionVec->getHeight())); - assert(pCurColor->getSampleCount() == 1 && pPrevColor->getSampleCount() == 1 && pMotionVec->getSampleCount() == 1); - - mpCB[mVarLocations.alpha] = mControls.alpha; - mpCB[mVarLocations.colorBoxSigma] = mControls.colorBoxSigma; - - ParameterBlock* pDefaultBlock = mpProgVars->getDefaultBlock().get(); - pDefaultBlock->setSrv(mVarLocations.colorTex, 0, pCurColor->getSRV()); - pDefaultBlock->setSrv(mVarLocations.prevColorTex, 0, pPrevColor->getSRV()); - pDefaultBlock->setSrv(mVarLocations.motionVecTex, 0, pMotionVec->getSRV()); - pDefaultBlock->setSampler(mVarLocations.sampler, 0, mpLinearSampler); - } - - void TemporalAA::execute(RenderContext* pRenderContext, const Texture::SharedPtr & pCurColor, const Texture::SharedPtr & pPrevColor, const Texture::SharedPtr & pMotionVec) - { - setVarsData(pCurColor, pPrevColor, pMotionVec); - pRenderContext->pushGraphicsVars(mpProgVars); - mpProgram->execute(pRenderContext); - pRenderContext->popGraphicsVars(); - } - - static const std::string& kMotionVec = "motionVecs"; - static const std::string& kColorIn = "colorIn"; - static const std::string& kColorOut = "colorOut"; - - RenderPassReflection TemporalAA::reflect() const - { - RenderPassReflection reflection; - reflection.addInput(kMotionVec, "Screen-space motion vectors"); - reflection.addInput(kColorIn, "Color-buffer of the current frame"); - reflection.addOutput(kColorOut, "Anti-aliased color buffer"); - return reflection; - } - - void TemporalAA::allocatePrevColor(const Texture* pColorOut) - { - bool allocate = mpPrevColor == nullptr; - allocate = allocate || (mpPrevColor->getWidth() != pColorOut->getWidth()); - allocate = allocate || (mpPrevColor->getHeight() != pColorOut->getHeight()); - allocate = allocate || (mpPrevColor->getDepth() != pColorOut->getDepth()); - allocate = allocate || (mpPrevColor->getFormat() != pColorOut->getFormat()); - assert(pColorOut->getSampleCount() == 1); - - if (allocate) - { - mpPrevColor = Texture::create2D(pColorOut->getWidth(), pColorOut->getHeight(), pColorOut->getFormat(), 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); - } - } - - void TemporalAA::execute(RenderContext* pContext, const RenderData* pData) - { - const auto& pColorIn = pData->getTexture(kColorIn); - const auto& pColorOut = pData->getTexture(kColorOut); - const auto& pMotionVec = pData->getTexture(kMotionVec); - allocatePrevColor(pColorOut.get()); - - Fbo::SharedPtr pFbo = Fbo::create(); - pFbo->attachColorTarget(pColorOut, 0); - pContext->getGraphicsState()->pushFbo(pFbo); - execute(pContext, pColorIn, mpPrevColor, pMotionVec); - pContext->getGraphicsState()->popFbo(); - - pContext->blit(pColorOut->getSRV(), mpPrevColor->getRTV()); - } -} \ No newline at end of file diff --git a/Framework/Source/Effects/TAA/TAA.h b/Framework/Source/Effects/TAA/TAA.h deleted file mode 100644 index d96e58fdd..000000000 --- a/Framework/Source/Effects/TAA/TAA.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "API/FBO.h" -#include "Graphics/FullScreenPass.h" -#include "Graphics/Program/ProgramVars.h" - -namespace Falcor -{ - class Gui; - - /** Temporal AA class - */ - class TemporalAA : public RenderPass, public inherit_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - static const char* kDesc; - - /** Destructor - */ - ~TemporalAA(); - - /** Create a new instance - */ - static SharedPtr create(const Dictionary& dict = {}); - - /** Render UI controls for this effect. - \param[in] pGui GUI object to render UI elements with - */ - virtual void renderUI(Gui* pGui, const char* uiGroup = "") override; - - /** Run the effect - \param[in] pRenderContext Render context with the destination FBO already set - \param[in] pCurColor Current frame color buffer - \param[in] pPrevColor Previous frame color buffer - \param[in] pMotionVec Motion vector buffer - */ - void execute(RenderContext* pRenderContext, const Texture::SharedPtr & pCurColor, const Texture::SharedPtr & pPrevColor, const Texture::SharedPtr & pMotionVec); - - /** Sets the alpha value used to blend the previous frame with the current frame. Lower values means previous frame has more weight - */ - void setAlphaValue(float alpha) { mControls.alpha = alpha; } - - /** Sets the sigma value - */ - void setColorBoxSigma(float sigma) { mControls.colorBoxSigma = sigma; } - - // Render-pass stuff - virtual void execute(RenderContext* pContext, const RenderData* pData) override; - virtual RenderPassReflection reflect() const override; - std::string getDesc() override { return kDesc; } - private: - TemporalAA(); - - // Create the Program. - void createProgram(); - - // Set the Variable Data needed for Rendering. - void setVarsData(const Texture::SharedPtr & pCurColor, const Texture::SharedPtr & pPrevColor, const Texture::SharedPtr & pMotionVec); - - FullScreenPass::UniquePtr mpProgram; - GraphicsVars::SharedPtr mpProgVars; - ConstantBuffer::SharedPtr mpCB; - Sampler::SharedPtr mpLinearSampler; - - struct - { - ParameterBlock::BindLocation colorTex; - ParameterBlock::BindLocation prevColorTex; - ParameterBlock::BindLocation motionVecTex; - ParameterBlock::BindLocation sampler; - size_t alpha; - size_t colorBoxSigma; - } mVarLocations; - - struct Controls - { - float alpha = 0.1f; - float colorBoxSigma = 1.0f; - }; - - // - Controls mControls; - - Texture::SharedPtr mpPrevColor; - void allocatePrevColor(const Texture* pColorOut); - }; -} \ No newline at end of file diff --git a/Framework/Source/Effects/ToneMapping/ToneMapping.cpp b/Framework/Source/Effects/ToneMapping/ToneMapping.cpp deleted file mode 100644 index 11762df8e..000000000 --- a/Framework/Source/Effects/ToneMapping/ToneMapping.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "ToneMapping.h" -#include "API/RenderContext.h" -#include "Graphics/FboHelper.h" - -namespace Falcor -{ - const char* ToneMapping::kDesc = "Tone-map a color-buffer. The resulting buffer is always in the [0, 1] range. The pass supports auto-exposure and eye-adaptation"; - - static const char* kShaderFilename = "Effects/ToneMapping.ps.slang"; - const Gui::DropdownList kOperatorList = { - { (uint32_t)ToneMapping::Operator::Clamp, "Clamp to LDR" }, - { (uint32_t)ToneMapping::Operator::Linear, "Linear" }, - { (uint32_t)ToneMapping::Operator::Reinhard, "Reinhard" }, - { (uint32_t)ToneMapping::Operator::ReinhardModified, "Modified Reinhard" }, - { (uint32_t)ToneMapping::Operator::HejiHableAlu, "Heji's approximation" }, - { (uint32_t)ToneMapping::Operator::HableUc2, "Uncharted 2" }, - { (uint32_t)ToneMapping::Operator::Aces, "ACES" } - }; - - static const std::string kOperator = "operator"; - - ToneMapping::~ToneMapping() = default; - - ToneMapping::ToneMapping(ToneMapping::Operator op) : RenderPass("ToneMapping") - { - createLuminancePass(); - createToneMapPass(op); - - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - mpPointSampler = Sampler::create(samplerDesc); - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - mpLinearSampler = Sampler::create(samplerDesc); - } - - ToneMapping::SharedPtr ToneMapping::create(Operator op) - { - ToneMapping* pTM = new ToneMapping(op); - return ToneMapping::SharedPtr(pTM); - } - - ToneMapping::SharedPtr ToneMapping::create(const Dictionary& dict) - { - Operator op = Operator::Aces; - for (const auto& v : dict) - { - if (v.key() == kOperator) op = v.val(); - else logWarning("Unknown field `" + v.key() + "` in a ToneMapping dictionary"); - } - return create(op); - } - - Dictionary ToneMapping::getScriptingDictionary() const - { - Dictionary d; - d[kOperator] = mOperator; - return d; - } - - void ToneMapping::createLuminanceFbo(const Texture::SharedPtr& pSrc) - { - bool createFbo = mpLuminanceFbo == nullptr; - ResourceFormat srcFormat = pSrc->getFormat(); - uint32_t bytesPerChannel = getFormatBytesPerBlock(srcFormat) / getFormatChannelCount(srcFormat); - - // Find the required texture size and format - ResourceFormat luminanceFormat = (bytesPerChannel == 32) ? ResourceFormat::R32Float : ResourceFormat::R16Float; - uint32_t requiredHeight = getLowerPowerOf2(pSrc->getHeight()); - uint32_t requiredWidth = getLowerPowerOf2(pSrc->getWidth()); - - if(createFbo == false) - { - createFbo = (requiredWidth != mpLuminanceFbo->getWidth()) || - (requiredHeight != mpLuminanceFbo->getHeight()) || - (luminanceFormat != mpLuminanceFbo->getColorTexture(0)->getFormat()); - } - - if(createFbo) - { - Fbo::Desc desc; - desc.setColorTarget(0, luminanceFormat); - mpLuminanceFbo = FboHelper::create2D(requiredWidth, requiredHeight, desc, 1, Fbo::kAttachEntireMipLevel); - } - } - - void ToneMapping::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pSrc, const Fbo::SharedPtr& pDst) - { - GraphicsState::SharedPtr pState = pRenderContext->getGraphicsState(); - createLuminanceFbo(pSrc); - - //Set shared vars - mpToneMapVars->getDefaultBlock()->setSrv(mBindLocations.colorTex, 0, pSrc->getSRV()); - mpLuminanceVars->getDefaultBlock()->setSrv(mBindLocations.colorTex, 0, pSrc->getSRV()); - mpToneMapVars->getDefaultBlock()->setSampler(mBindLocations.colorSampler, 0, mpPointSampler); - mpLuminanceVars->getDefaultBlock()->setSampler(mBindLocations.colorSampler, 0, mpLinearSampler); - - //Calculate luminance - pRenderContext->setGraphicsVars(mpLuminanceVars); - pState->setFbo(mpLuminanceFbo); - mpLuminancePass->execute(pRenderContext); - mpLuminanceFbo->getColorTexture(0)->generateMips(pRenderContext); - - //Set Tone map vars - if (mOperator != Operator::Clamp) - { - mpToneMapCBuffer->setBlob(&mConstBufferData, 0u, sizeof(mConstBufferData)); - mpToneMapVars->getDefaultBlock()->setSampler(mBindLocations.luminanceSampler, 0, mpLinearSampler); - mpToneMapVars->getDefaultBlock()->setSrv(mBindLocations.luminanceTex, 0, mpLuminanceFbo->getColorTexture(0)->getSRV()); - } - - //Tone map - pRenderContext->setGraphicsVars(mpToneMapVars); - pState->setFbo(pDst); - mpToneMapPass->execute(pRenderContext); - } - - void ToneMapping::createToneMapPass(ToneMapping::Operator op) - { - mpToneMapPass = FullScreenPass::create(kShaderFilename); - - mOperator = op; - switch(op) - { - case Operator::Clamp: - mpToneMapPass->getProgram()->addDefine("_CLAMP"); - break; - case Operator::Linear: - mpToneMapPass->getProgram()->addDefine("_LINEAR"); - break; - case Operator::Reinhard: - mpToneMapPass->getProgram()->addDefine("_REINHARD"); - break; - case Operator::ReinhardModified: - mpToneMapPass->getProgram()->addDefine("_REINHARD_MOD"); - break; - case Operator::HejiHableAlu: - mpToneMapPass->getProgram()->addDefine("_HEJI_HABLE_ALU"); - break; - case Operator::HableUc2: - mpToneMapPass->getProgram()->addDefine("_HABLE_UC2"); - break; - case Operator::Aces: - mpToneMapPass->getProgram()->addDefine("_ACES"); - break; - default: - should_not_get_here(); - } - - const auto& pReflector = mpToneMapPass->getProgram()->getReflector(); - mpToneMapVars = GraphicsVars::create(pReflector); - mpToneMapCBuffer = mpToneMapVars["PerImageCB"]; - const auto& pDefaultBlock = pReflector->getDefaultParameterBlock(); - mBindLocations.luminanceSampler = pDefaultBlock->getResourceBinding("gLuminanceTexSampler"); - mBindLocations.colorSampler = pDefaultBlock->getResourceBinding("gColorSampler"); - mBindLocations.colorTex = pDefaultBlock->getResourceBinding("gColorTex"); - mBindLocations.luminanceTex = pDefaultBlock->getResourceBinding("gLuminanceTex"); - } - - void ToneMapping::createLuminancePass() - { - mpLuminancePass = FullScreenPass::create(kShaderFilename); - mpLuminancePass->getProgram()->addDefine("_LUMINANCE"); - const auto& pReflector = mpLuminancePass->getProgram()->getReflector(); - mpLuminanceVars = GraphicsVars::create(pReflector); - } - - void ToneMapping::renderUI(Gui* pGui, const char* uiGroup) - { - if((uiGroup == nullptr) || pGui->beginGroup(uiGroup)) - { - uint32_t opIndex = static_cast(mOperator); - if (pGui->addDropdown("Operator", kOperatorList, opIndex)) - { - mOperator = static_cast(opIndex); - createToneMapPass(mOperator); - } - - pGui->addFloatVar("Exposure Key", mConstBufferData.exposureKey, 0.0001f, 200.0f); - pGui->addFloatVar("Luminance LOD", mConstBufferData.luminanceLod, 0, 16, 0.025f); - //Only give option to change these if the relevant operator is selected - if (mOperator == Operator::ReinhardModified) - { - pGui->addFloatVar("White Luminance", mConstBufferData.whiteMaxLuminance, 0.1f, FLT_MAX, 0.2f); - } - else if (mOperator == Operator::HableUc2) - { - pGui->addFloatVar("Linear White", mConstBufferData.whiteScale, 0, 100, 0.01f); - } - - if (uiGroup) pGui->endGroup(); - } - } - - void ToneMapping::setOperator(Operator op) - { - if(op != mOperator) - { - createToneMapPass(op); - } - } - - void ToneMapping::setExposureKey(float exposureKey) - { - mConstBufferData.exposureKey = max(0.001f, exposureKey); - } - - void ToneMapping::setWhiteMaxLuminance(float maxLuminance) - { - mConstBufferData.whiteMaxLuminance = maxLuminance; - } - - void ToneMapping::setLuminanceLod(float lod) - { - mConstBufferData.luminanceLod = clamp(lod, 0.0f, 16.0f); - } - - void ToneMapping::setWhiteScale(float whiteScale) - { - mConstBufferData.whiteScale = max(0.001f, whiteScale); - } - - static const std::string kSrc = "src"; - static const std::string kDst = "dst"; - - RenderPassReflection ToneMapping::reflect() const - { - RenderPassReflection reflector; - reflector.addInput(kSrc, "Source texture"); - reflector.addOutput(kDst, "Tone-mapped output texture"); - return reflector; - } - - void ToneMapping::execute(RenderContext* pRenderContext, const RenderData* pData) - { - Fbo::SharedPtr pFbo = Fbo::create(); - pFbo->attachColorTarget(pData->getTexture(kDst), 0); - - execute(pRenderContext, pData->getTexture(kSrc), pFbo); - } -} \ No newline at end of file diff --git a/Framework/Source/Effects/ToneMapping/ToneMapping.h b/Framework/Source/Effects/ToneMapping/ToneMapping.h deleted file mode 100644 index 78c2e2b84..000000000 --- a/Framework/Source/Effects/ToneMapping/ToneMapping.h +++ /dev/null @@ -1,194 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Graphics/FullScreenPass.h" -#include "API/ConstantBuffer.h" -#include "API/FBO.h" -#include "API/Sampler.h" -#include "Utils/Gui.h" -#include "Experimental/RenderGraph/RenderPass.h" - -namespace Falcor -{ - /** Tone-mapping effect - */ - class ToneMapping : public RenderPass, public inherit_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - static const char* kDesc; - - /** Destructor - */ - ~ToneMapping(); - - /** The tone-mapping operator to use - */ - enum class Operator - { - Clamp, ///< Clamp to [0, 1]. Just like LDR - Linear, ///< Linear mapping - Reinhard, ///< Reinhard operator - ReinhardModified, ///< Reinhard operator with maximum white intensity - HejiHableAlu, ///< John Hable's ALU approximation of Jim Heji's filmic operator - HableUc2, ///< John Hable's filmic tone-mapping used in Uncharted 2 - Aces, ///< Aces Filmic Tone-Mapping - }; - - /** Create a new object - */ - static SharedPtr create(Operator op = Operator::Aces); - static SharedPtr create(const Dictionary& dict); - - /** Render UI elements - \param[in] pGui GUI instance to render UI with - \param[in] uiGroup Name for the group to render UI elements within - */ - void renderUI(Gui* pGui, const char* uiGroup) override; - - /** Run the tone-mapping program - \param pRenderContext Render-context to use - \param pSrc The source texture - \param pDst The destination FBO - */ - void execute(RenderContext* pRenderContext, const Texture::SharedPtr& pSrc, const Fbo::SharedPtr& pDst); - - /** Set a new operator. Triggers shader recompilation if operator has not been set on this instance before. - */ - void setOperator(Operator op); - - /** Sets the middle-gray luminance used for normalizing each pixel's luminance. - Middle gray is usually in the range of [0.045, 0.72]. - Lower values maximize contrast. Useful for night scenes. - Higher values minimize contrast, resulting in brightly lit objects. - */ - void setExposureKey(float exposureKey); - - /** Sets the maximal luminance to be consider as pure white. - Only valid if the operator is ReinhardModified - */ - void setWhiteMaxLuminance(float maxLuminance); - - /** Sets the luminance texture LOD to use when fetching average luminance values. - Lower values will result in a more localized effect - */ - void setLuminanceLod(float lod); - - /** Sets the white-scale used in Uncharted 2 tone mapping. - */ - void setWhiteScale(float whiteScale); - - /** Called once before compilation. Describes I/O requirements of the pass. - The requirements can't change after the graph is compiled. If the IO requests are dynamic, you'll need to trigger compilation of the render-graph yourself. - */ - virtual RenderPassReflection reflect() const override; - - /** Executes the pass. - */ - virtual void execute(RenderContext* pRenderContext, const RenderData* pData) override; - - /** Get the tonemapping operator type. - */ - Operator getOperator() const { return mOperator; } - - /** Get tonemapper exposure key value. - */ - float getExposureKey() const { return mConstBufferData.exposureKey; } - - /** Gets the maximal luminance to be consider as pure white. - */ - float getWhiteMaxLuminance() const { return mConstBufferData.whiteMaxLuminance; } - - /** Gets the luminance texture LOD to use when fetching average luminance values. - */ - float getLuminanceLod() const { return mConstBufferData.luminanceLod; } - - /** Gets the white-scale used in Uncharted 2 tone mapping. - */ - float getWhiteScale() const { return mConstBufferData.whiteScale; } - - /** Get the scripting dictionary - */ - Dictionary getScriptingDictionary() const override; - - /** Get a description of the pass - */ - std::string getDesc() override { return kDesc; } - private: - ToneMapping(Operator op); - void createLuminanceFbo(const Texture::SharedPtr& pSrc); - - Operator mOperator; - FullScreenPass::UniquePtr mpToneMapPass; - FullScreenPass::UniquePtr mpLuminancePass; - Fbo::SharedPtr mpLuminanceFbo; - GraphicsVars::SharedPtr mpToneMapVars; - GraphicsVars::SharedPtr mpLuminanceVars; - ConstantBuffer::SharedPtr mpToneMapCBuffer; - Sampler::SharedPtr mpPointSampler; - Sampler::SharedPtr mpLinearSampler; - - struct PassBindLocations - { - ParameterBlockReflection::BindLocation luminanceSampler; - ParameterBlockReflection::BindLocation colorSampler; - ParameterBlockReflection::BindLocation colorTex; - ParameterBlockReflection::BindLocation luminanceTex; - } mBindLocations; - - struct - { - float exposureKey = 0.042f; - float whiteMaxLuminance = 1.0f; - float luminanceLod = 16; // Max possible LOD, will result in global operation - float whiteScale = 11.2f; - } mConstBufferData; - - void createToneMapPass(Operator op); - void createLuminancePass(); - }; - -#define tonemap_op(a) case ToneMapping::Operator::a: return #a - inline std::string to_string(ToneMapping::Operator op) - { - switch (op) - { - tonemap_op(Clamp); - tonemap_op(Linear); - tonemap_op(Reinhard); - tonemap_op(ReinhardModified); - tonemap_op(HejiHableAlu); - tonemap_op(HableUc2); - tonemap_op(Aces); - default: - should_not_get_here(); - return ""; - } - } -#undef tonemap_op -} \ No newline at end of file diff --git a/Framework/Source/Effects/Utils/GaussianBlur.cpp b/Framework/Source/Effects/Utils/GaussianBlur.cpp deleted file mode 100644 index f1cfc6f7b..000000000 --- a/Framework/Source/Effects/Utils/GaussianBlur.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "GaussianBlur.h" -#include "API/RenderContext.h" -#include "Graphics/FboHelper.h" -#include "Utils/Gui.h" -#define _USE_MATH_DEFINES -#include - -namespace Falcor -{ - static std::string kShaderFilename("Effects/GaussianBlur.ps.slang"); - - GaussianBlur::~GaussianBlur() = default; - - GaussianBlur::GaussianBlur(uint32_t kernelWidth, float sigma) : mKernelWidth(kernelWidth), mSigma(sigma) - { - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpSampler = Sampler::create(samplerDesc); - } - - GaussianBlur::UniquePtr GaussianBlur::create(uint32_t kernelSize, float sigma) - { - GaussianBlur* pBlur = new GaussianBlur(kernelSize, sigma); - return GaussianBlur::UniquePtr(pBlur); - } - - void GaussianBlur::renderUI(Gui* pGui, const char* uiGroup) - { - if (uiGroup == nullptr || pGui->beginGroup(uiGroup)) - { - if (pGui->addIntVar("Kernel Width", (int&)mKernelWidth, 1, 15, 2)) - { - setKernelWidth(mKernelWidth); - } - if (pGui->addFloatVar("Sigma", mSigma, 0.001f)) - { - setSigma(mSigma); - } - if (uiGroup) pGui->endGroup(); - } - } - - void GaussianBlur::setKernelWidth(uint32_t kernelWidth) - { - mKernelWidth = kernelWidth | 1; // Make sure the kernel width is an odd number - mDirty = true; - } - - float getCoefficient(float sigma, float kernelWidth, float x) - { - float sigmaSquared = sigma * sigma; - float p = -(x*x) / (2 * sigmaSquared); - float e = exp(p); - - float a = 2 * (float)M_PI * sigmaSquared; - return e / a; - } - - void GaussianBlur::updateKernel() - { - uint32_t center = mKernelWidth / 2; - float sum = 0; - std::vector weights(center + 1); - for (uint32_t i = 0; i <= center; i++) - { - weights[i] = getCoefficient(mSigma, (float)mKernelWidth, (float)i); - sum += (i == 0) ? weights[i] : 2 * weights[i]; - } - - TypedBuffer::SharedPtr pBuf = TypedBuffer::create(mKernelWidth, Resource::BindFlags::ShaderResource); - - for (uint32_t i = 0; i <= center; i++) - { - float w = weights[i] / sum; - pBuf[center + i] = w; - pBuf[center - i] = w; - } - mpVars->setTypedBuffer("weights", pBuf); - } - - void GaussianBlur::createTmpFbo(const Texture* pSrc) - { - bool createFbo = mpTmpFbo == nullptr; - ResourceFormat srcFormat = pSrc->getFormat(); - - if(createFbo == false) - { - createFbo = (pSrc->getWidth() != mpTmpFbo->getWidth()) || - (pSrc->getHeight() != mpTmpFbo->getHeight()) || - (srcFormat != mpTmpFbo->getColorTexture(0)->getFormat()) || - pSrc->getArraySize() != mpTmpFbo->getColorTexture(0)->getArraySize(); - } - - if(createFbo) - { - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, srcFormat); - mpTmpFbo = FboHelper::create2D(pSrc->getWidth(), pSrc->getHeight(), fboDesc, pSrc->getArraySize()); - } - } - - void GaussianBlur::createProgram() - { - Program::DefineList defines; - defines.add("_KERNEL_WIDTH", std::to_string(mKernelWidth)); - if(mpTmpFbo->getColorTexture(0)->getArraySize() > 1) - { - defines.add("_USE_TEX2D_ARRAY"); - } - - uint32_t arraySize = mpTmpFbo->getColorTexture(0)->getArraySize(); - uint32_t layerMask = (arraySize > 1) ? ((1 << arraySize) - 1) : 0; - mpHorizontalBlur = FullScreenPass::create(kShaderFilename, defines, true, true, layerMask); - mpHorizontalBlur->getProgram()->addDefine("_HORIZONTAL_BLUR"); - mpVerticalBlur = FullScreenPass::create(kShaderFilename, defines, true, true, layerMask); - mpVerticalBlur->getProgram()->addDefine("_VERTICAL_BLUR"); - - ProgramReflection::SharedConstPtr pReflector = mpHorizontalBlur->getProgram()->getReflector(); - mpVars = GraphicsVars::create(pReflector); - - mBindLocations.sampler = pReflector->getDefaultParameterBlock()->getResourceBinding("gSampler"); - mBindLocations.srcTexture = pReflector->getDefaultParameterBlock()->getResourceBinding("gSrcTex"); - - updateKernel(); - mDirty = false; - } - - void GaussianBlur::execute(RenderContext* pRenderContext, Texture::SharedPtr pSrc, Fbo::SharedPtr pDst) - { - createTmpFbo(pSrc.get()); - if (mDirty) - { - createProgram(); - } - - uint32_t arraySize = pSrc->getArraySize(); - GraphicsState::Viewport vp; - vp.originX = 0; - vp.originY = 0; - vp.height = (float)mpTmpFbo->getHeight(); - vp.width = (float)mpTmpFbo->getWidth(); - vp.minDepth = 0; - vp.maxDepth = 1; - - GraphicsState* pState = pRenderContext->getGraphicsState().get(); - for(uint32_t i = 0; i < arraySize; i++) - { - pState->pushViewport(i, vp); - } - - // Horizontal pass - ParameterBlock* pDefaultBlock = mpVars->getDefaultBlock().get(); - pDefaultBlock->setSampler(mBindLocations.sampler, 0, mpSampler); - pDefaultBlock->setSrv(mBindLocations.srcTexture, 0, pSrc->getSRV()); - - pState->pushFbo(mpTmpFbo); - pRenderContext->pushGraphicsVars(mpVars); - mpHorizontalBlur->execute(pRenderContext); - - // Vertical pass - pDefaultBlock->setSrv(mBindLocations.srcTexture, 0, mpTmpFbo->getColorTexture(0)->getSRV()); - pRenderContext->setGraphicsVars(mpVars); - pState->setFbo(pDst); - mpVerticalBlur->execute(pRenderContext); - - pState->popFbo(); - for(uint32_t i = 0; i < arraySize; i++) - { - pState->popViewport(i); - } - - pRenderContext->popGraphicsVars(); - } -} \ No newline at end of file diff --git a/Framework/Source/Effects/Utils/GaussianBlur.h b/Framework/Source/Effects/Utils/GaussianBlur.h deleted file mode 100644 index ab51553bd..000000000 --- a/Framework/Source/Effects/Utils/GaussianBlur.h +++ /dev/null @@ -1,111 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "API/FBO.h" -#include "Graphics/FullScreenPass.h" -#include "API/Sampler.h" -#include "Graphics/Program/ProgramVars.h" -#include - -namespace Falcor -{ - class RenderContext; - class Texture; - class Fbo; - class Gui; - - /** Gaussian-blur technique - */ - class GaussianBlur - { - public: - using UniquePtr = std::unique_ptr; - /** Destructor - */ - ~GaussianBlur(); - - /** Create a new object - \param[in] kernelSize Number of samples taken along each axis - \param[in] sigma Gaussian distribution sigma value used to calculate sample weights. Values smaller than twice the sigma are ineffective - */ - static UniquePtr create(uint32_t kernelSize = 5, float sigma = 2.5f); - - /** Apply gaussian blur by rendering one texture into another. - \param pRenderContext Render context to use - \param pSrc The source texture - \param pDst The destination texture - */ - void execute(RenderContext* pRenderContext, Texture::SharedPtr pSrc, Fbo::SharedPtr pDst); - - /** Set the kernel width. Controls the number of texels which will be sampled when blurring each pixel. - Values smaller than twice the sigma are ineffective. - */ - void setKernelWidth(uint32_t kernelWidth); - - /** Get the kernel width - */ - uint32_t getKernelWidth() const { return mKernelWidth; } - - /** Set the sigma. Higher values will result in a blurrier image. - Values greater than half the kernel width are ineffective. - */ - void setSigma(float sigma) { mSigma = sigma; mDirty = true; } - - /** Get the sigma value. - */ - float getSigma() const { return mSigma; } - - /** Render UI controls for blur settings. - \param[in] pGui GUI instance to render UI elements with - \param[in] uiGroup Optional name. If specified, UI elements will be rendered within a named group - */ - void renderUI(Gui* pGui, const char* uiGroup = nullptr); - - private: - GaussianBlur(uint32_t kernelSize, float sigma); - uint32_t mKernelWidth; - float mSigma; - uint32_t vpMask; - void createTmpFbo(const Texture* pSrc); - void createProgram(); - void updateKernel(); - - FullScreenPass::UniquePtr mpHorizontalBlur; - FullScreenPass::UniquePtr mpVerticalBlur; - Fbo::SharedPtr mpTmpFbo; - Sampler::SharedPtr mpSampler; - bool mDirty = true; - GraphicsVars::SharedPtr mpVars; - - struct - { - ParameterBlockReflection::BindLocation sampler; - ParameterBlockReflection::BindLocation srcTexture; - } mBindLocations; - }; -} \ No newline at end of file diff --git a/Framework/Source/Experimental/Raytracing/RtModel.cpp b/Framework/Source/Experimental/Raytracing/RtModel.cpp deleted file mode 100644 index b7ece42bf..000000000 --- a/Framework/Source/Experimental/Raytracing/RtModel.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "RtModel.h" -#include "API/Device.h" -#include "API/RenderContext.h" -#include "API/LowLevel/LowLevelContextData.h" -#include "API/VAO.h" - -namespace Falcor -{ - RtModel::RtModel(const Model& model, RtBuildFlags buildFlags) : mBuildFlags(buildFlags), Model(model) - { - } - - // TODO: Static meshes with materials that differ with respect to the doubleSided flag should not be grouped. - void RtModel::createBottomLevelData() - { - // The logic works as follows: - // - Static meshes come before dynamic meshes - // - Meshes that have a single instance and have the same matrix are contiguous - - // Store the meshes into lists, grouped based on their transformation matrix - std::list staticMeshes; - std::list dynamicMeshes; - for (const auto& instanceList : mMeshes) - { - assert(instanceList.size() > 0); - if (instanceList[0]->getObject()->hasBones()) - { - dynamicMeshes.push_back(instanceList); - continue; - } - - // If we have multiple instances, push it to the end of the list - if (instanceList.size() > 1) - { - staticMeshes.push_back(instanceList); - } - else - { - bool handled = false; - // Find the insert location. Should have a single instance and the matrix should match - for (auto& it = staticMeshes.begin(); it != staticMeshes.end(); it++) - { - if (it->size() > 1) break; - if ((*it)[0]->getTransformMatrix() == instanceList[0]->getTransformMatrix()) - { - handled = true; - staticMeshes.insert(it, instanceList); - break; - } - } - if (!handled) staticMeshes.push_front(instanceList); - } - } - - // Copy the lists into the vectors. Static meshes first - size_t count = mMeshes.size(); - assert(staticMeshes.size() + dynamicMeshes.size() == count); - mMeshes.clear(); - mMeshes.reserve(count); - - auto insertFunc = [this](const auto& meshList, bool isStatic) - { - if (meshList.size()) - { - BottomLevelData data; - data.isStatic = isStatic; - mat4 transformation = (*meshList.begin())[0]->getTransformMatrix(); - data.meshCount = 0; - data.meshBaseIndex = (uint32_t)mMeshes.size(); - bool instanced = false; - for (auto& it : meshList) - { - // The logic works as follows: - // - Dynamic meshes all go in the same group, as they don't have individual mesh-instance transforms. The skinning code computes their vertices. - // - Static non-instanced meshes are grouped per instance transform - // - Static instanced meshes all go in invididual groups, and come after non-instanced meshes in the list. - - if (isStatic) - { - // Validate that instanced meshes are last - if (it.size() > 1) instanced = true; - else assert(!instanced); - - // If mesh is instanced or the transform has changed, start a new mesh group - if (it.size() > 1 || it[0]->getTransformMatrix() != transformation) - { - if (data.meshCount > 0) // The first mesh could be instanced... - { - mBottomLevelData.push_back(data); - } - - transformation = it[0]->getTransformMatrix(); - data.meshBaseIndex = (uint32_t)mMeshes.size(); - data.meshCount = 0; - } - } - else - { - // We don't handle dynamic instanced meshes - assert(it.size() == 1); - } - - mMeshes.push_back(it); - data.meshCount++; - } - mBottomLevelData.push_back(data); - } - }; - - insertFunc(staticMeshes, true); - insertFunc(dynamicMeshes, false); - - // Validate that mBottomLevelData represents a contiguous range that includes all meshes, and that grouped meshes are non-instanced - uint32_t baseIdx = 0; - for (auto& it : mBottomLevelData) - { - assert(it.meshCount > 0); - assert(it.meshBaseIndex + it.meshCount <= mMeshes.size()); - for (uint32_t idx = it.meshBaseIndex; idx < it.meshBaseIndex + it.meshCount; idx++) - { - assert(it.meshCount == 1 || mMeshes[idx].size() == 1); - } - assert(it.meshBaseIndex == baseIdx); - baseIdx += it.meshCount; - } - assert(baseIdx == mMeshes.size()); - } - - RtModel::SharedPtr RtModel::createFromModel(const Model& model, RtBuildFlags buildFlags) - { - SharedPtr pRtModel = SharedPtr(new RtModel(model, buildFlags)); - pRtModel->createBottomLevelData(); - - // If model is skinned, postpone build until after animate() so we have valid skinned vertices - if (!pRtModel->hasBones()) - { - pRtModel->buildAccelerationStructure(); - } - return pRtModel; - } - - bool RtModel::update() - { - // Call base class to compute skinned vertices - if (Model::update()) - { - buildAccelerationStructure(); - return true; - } - return false; - } - - void RtModel::buildAccelerationStructure() - { - RenderContext* pContext = gpDevice->getRenderContext(); - - auto dxrFlags = getDxrBuildFlags(mBuildFlags); - - // Create an AS for each mesh-group - for (auto& blasData : mBottomLevelData) - { - std::vector geomDesc(blasData.meshCount); - for (size_t meshIndex = blasData.meshBaseIndex; meshIndex < blasData.meshBaseIndex + blasData.meshCount; meshIndex++) - { - assert(meshIndex < mMeshes.size()); - const Mesh* pMesh = getMesh((uint32_t)meshIndex).get(); - - D3D12_RAYTRACING_GEOMETRY_DESC& desc = geomDesc[meshIndex - blasData.meshBaseIndex]; - desc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; - desc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_NONE; - desc.Triangles.Transform3x4 = 0; - - // Get the position VB - const Vao* pVao = getMeshVao(pMesh).get(); - const auto& elemDesc = pVao->getElementIndexByLocation(VERTEX_POSITION_LOC); - const auto& pVbLayout = pVao->getVertexLayout()->getBufferLayout(elemDesc.vbIndex); - - const Buffer* pVB = pVao->getVertexBuffer(elemDesc.vbIndex).get(); - pContext->resourceBarrier(pVB, Resource::State::NonPixelShader); - desc.Triangles.VertexBuffer.StartAddress = pVB->getGpuAddress() + pVbLayout->getElementOffset(elemDesc.elementIndex); - desc.Triangles.VertexBuffer.StrideInBytes = pVbLayout->getStride(); - desc.Triangles.VertexCount = pMesh->getVertexCount(); - desc.Triangles.VertexFormat = getDxgiFormat(pVbLayout->getElementFormat(elemDesc.elementIndex)); - - // Get the IB - const Buffer* pIB = pVao->getIndexBuffer().get(); - pContext->resourceBarrier(pIB, Resource::State::NonPixelShader); - desc.Triangles.IndexBuffer = pIB->getGpuAddress(); - desc.Triangles.IndexCount = pMesh->getIndexCount(); - desc.Triangles.IndexFormat = getDxgiFormat(pVao->getIndexBufferFormat()); - - // If this is an opaque mesh, set the opaque flag - if (pMesh->getMaterial()->getAlphaMode() == AlphaModeOpaque) - { - desc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE; - } - } - - // Create the acceleration and aux buffers - D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS inputs = {}; - inputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; - inputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE; - inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; - inputs.NumDescs = (uint32_t)geomDesc.size(); - inputs.pGeometryDescs = geomDesc.data(); - - D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO info; - GET_COM_INTERFACE(gpDevice->getApiHandle(), ID3D12Device5, pDevice5); - pDevice5->GetRaytracingAccelerationStructurePrebuildInfo(&inputs, &info); - - Buffer::SharedPtr pScratchBuffer = Buffer::create(info.ScratchDataSizeInBytes, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); - blasData.pBlas = Buffer::create(info.ResultDataMaxSizeInBytes, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); - - // Build the AS - D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC asDesc = {}; - asDesc.Inputs = inputs; - asDesc.DestAccelerationStructureData = blasData.pBlas->getGpuAddress(); - asDesc.ScratchAccelerationStructureData = pScratchBuffer->getGpuAddress(); - - GET_COM_INTERFACE(pContext->getLowLevelData()->getCommandList(), ID3D12GraphicsCommandList4, pList4); - pList4->BuildRaytracingAccelerationStructure(&asDesc, 0, nullptr); - - // Insert a UAV barrier - pContext->uavBarrier(blasData.pBlas.get()); - } - } - - RtModel::SharedPtr RtModel::createFromFile(const char* filename, RtBuildFlags buildFlags, Model::LoadFlags flags) - { - Model::SharedPtr pModel = Model::createFromFile(filename, flags); - if (!pModel) return nullptr; - - return createFromModel(*pModel, buildFlags); - } -}; \ No newline at end of file diff --git a/Framework/Source/Experimental/Raytracing/RtScene.cpp b/Framework/Source/Experimental/Raytracing/RtScene.cpp deleted file mode 100644 index de559a49d..000000000 --- a/Framework/Source/Experimental/Raytracing/RtScene.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "RtScene.h" -#include "Graphics/Scene/SceneImporter.h" -#include "API/DescriptorSet.h" -#include "API/Device.h" - -namespace Falcor -{ - RtScene::SharedPtr RtScene::loadFromFile(const std::string& filename, RtBuildFlags rtFlags, Model::LoadFlags modelLoadFlags, Scene::LoadFlags sceneLoadFlags) - { - RtScene::SharedPtr pRtScene = create(rtFlags); - if (SceneImporter::loadScene(*pRtScene, filename, modelLoadFlags | Model::LoadFlags::BuffersAsShaderResource, sceneLoadFlags) == false) - { - pRtScene = nullptr; - } - - int count = 0; - for (auto& path : pRtScene->mpPaths) - { - for (uint32_t objIdx = 0u; objIdx < path->getAttachedObjectCount(); objIdx++) - { - const auto& it = pRtScene->mModelInstanceToRtModelInstance.find(path->getAttachedObject(objIdx).get()); - if (it != pRtScene->mModelInstanceToRtModelInstance.end()) - { - path->attachObject(it->second); - } - } - } - - pRtScene->mModelInstanceToRtModelInstance.clear(); - return pRtScene; - } - - RtScene::SharedPtr RtScene::create(RtBuildFlags rtFlags) - { - return SharedPtr(new RtScene(rtFlags)); - } - - RtScene::SharedPtr RtScene::createFromModel(RtModel::SharedPtr pModel) - { - SharedPtr pScene = RtScene::create(pModel->getBuildFlags()); - pScene->addModelInstance(pModel, "instance0"); - - return pScene; - } - - bool RtScene::update(double currentTime, CameraController* cameraController) - { - bool changed = Scene::update(currentTime, cameraController); - mTlasHitProgCount = mExtentsDirty ? -1 : mTlasHitProgCount; - - if (mEnableRefit) - { - mRefit = true; - } - return changed; - } - - void RtScene::addModelInstance(const ModelInstance::SharedPtr& pInstance) - { - RtModel::SharedPtr pRtModel = std::dynamic_pointer_cast(pInstance->getObject()); - if (pRtModel) - { - Scene::addModelInstance(pInstance); - } - else - { - // Check if we need to create a new model - const auto& it = mModelToRtModel.find(pInstance->getObject().get()); - if (it == mModelToRtModel.end()) - { - pRtModel = RtModel::createFromModel(*pInstance->getObject(), mRtFlags); - mModelToRtModel[pInstance->getObject().get()] = pRtModel; - } - else - { - pRtModel = it->second; - } - ModelInstance::SharedPtr pRtInstance = ModelInstance::create(pRtModel, pInstance->getTranslation(), pInstance->getTarget(), pInstance->getUpVector(), pInstance->getScaling(), pInstance->getName()); - Scene::addModelInstance(pRtInstance); - - // any paths attached to this ModelInstance need to be updated - int count = 0; - auto pMovable = std::dynamic_pointer_cast(pInstance); - auto pRtMovable = std::dynamic_pointer_cast(pRtInstance); - - mModelInstanceToRtModelInstance[pMovable.get()] = pRtMovable; - } - - // If we have skinned models, attach a skinning cache and animate the scene once to trigger a VB update - if (pRtModel->hasBones()) - { - pRtModel->attachSkinningCache(mpSkinningCache); - pRtModel->animate(0); - } - } - - std::vector RtScene::createInstanceDesc(const RtScene* pScene, uint32_t hitProgCount) - { - mGeometryCount = 0; - std::vector instanceDesc; - mModelInstanceData.resize(pScene->getModelCount()); - - uint32_t tlasIndex = 0; - uint32_t instanceContributionToHitGroupIndex = 0; - // Loop over all the models - for (uint32_t modelId = 0; modelId < pScene->getModelCount(); modelId++) - { - auto& modelInstanceData = mModelInstanceData[modelId]; - const RtModel* pModel = dynamic_cast(pScene->getModel(modelId).get()); - assert(pModel); // Can't work on regular models - modelInstanceData.modelBase = tlasIndex; - modelInstanceData.meshInstancesPerModelInstance = 0; - modelInstanceData.meshBase.resize(pModel->getMeshCount()); - - for (uint32_t modelInstance = 0; modelInstance < pScene->getModelInstanceCount(modelId); modelInstance++) - { - const auto& pModelInstance = pScene->getModelInstance(modelId, modelInstance); - // Loop over the meshes - for (uint32_t blasId = 0; blasId < pModel->getBottomLevelDataCount(); blasId++) - { - // Initialize the instance desc - const auto& blasData = pModel->getBottomLevelData(blasId); - D3D12_RAYTRACING_INSTANCE_DESC idesc = {}; - idesc.AccelerationStructure = blasData.pBlas->getGpuAddress(); - - // Set the meshes tlas offset - if (modelInstance == 0) - { - for (uint32_t i = 0; i < blasData.meshCount; i++) - { - assert(blasData.meshCount == 1 || pModel->getMeshInstanceCount(blasData.meshBaseIndex + i) == 1); // A BLAS shouldn't have multiple instanced meshes - modelInstanceData.meshBase[blasData.meshBaseIndex + i] = modelInstanceData.meshInstancesPerModelInstance + i; // If i>0 each mesh has a single instance - } - } - - uint32_t meshInstanceCount = pModel->getMeshInstanceCount(blasData.meshBaseIndex); - for (uint32_t meshInstance = 0; meshInstance < meshInstanceCount; meshInstance++) - { - idesc.InstanceID = uint32_t(instanceDesc.size()); - idesc.InstanceContributionToHitGroupIndex = instanceContributionToHitGroupIndex; - instanceContributionToHitGroupIndex += hitProgCount * blasData.meshCount; - idesc.InstanceMask = 0xff; - const auto& pMaterial = pModel->getMeshInstance(blasData.meshBaseIndex, meshInstance)->getObject()->getMaterial(); - idesc.Flags = D3D12_RAYTRACING_INSTANCE_FLAG_NONE; - - // TODO: This code is incorrect since a BLAS can have multiple meshes with different materials and hence different doubleSided flags. - if (pMaterial->isDoubleSided()) - { - idesc.Flags |= D3D12_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE; - } - - // Only apply mesh-instance transform on non-skinned meshes - mat4 transform = pModelInstance->getTransformMatrix(); - if (blasData.isStatic) - { - transform = transform * pModel->getMeshInstance(blasData.meshBaseIndex, meshInstance)->getTransformMatrix(); // If there are multiple meshes in a BLAS, they all have the same transform - } - transform = transpose(transform); - memcpy(idesc.Transform, &transform, sizeof(idesc.Transform)); - instanceDesc.push_back(idesc); - mGeometryCount += blasData.meshCount; - if (modelInstance == 0) modelInstanceData.meshInstancesPerModelInstance += blasData.meshCount; - tlasIndex += blasData.meshCount; - assert(tlasIndex * hitProgCount == instanceContributionToHitGroupIndex); - } - } - } - } - assert(tlasIndex == mGeometryCount); - - // Validate that our getInstanceId() helper returns contigous indices. - uint32_t instanceId = 0; - for (uint32_t model = 0; model < getModelCount(); model++) - { - for (uint32_t modelInstance = 0; modelInstance < getModelInstanceCount(model); modelInstance++) - { - for (uint32_t mesh = 0; mesh < getModel(model)->getMeshCount(); mesh++) - { - for (uint32_t meshInstance = 0; meshInstance < getModel(model)->getMeshInstanceCount(mesh); meshInstance++) - { - assert(getInstanceId(model, modelInstance, mesh, meshInstance) == instanceId++); - } - } - } - } - assert(instanceId == mGeometryCount); - - return instanceDesc; - } - - // TODO: Cache TLAS per hitProgCount, as some render pipelines need multiple TLAS:es with different #hit progs in same frame, currently that trigger rebuild every frame. See issue #365. - void RtScene::createTlas(uint32_t hitProgCount) - { - if (mTlasHitProgCount == hitProgCount) return; - mTlasHitProgCount = hitProgCount; - - // Early out if hit program count is zero or if scene is empty. - if (hitProgCount == 0 || getModelCount() == 0) - { - mModelInstanceData.clear(); - mpTopLevelAS = nullptr; - mTlasSrv = nullptr; - mGeometryCount = 0; - mInstanceCount = 0; - mRefit = false; - return; - } - - // todo: move this somewhere fair. - mRtFlags |= RtBuildFlags::AllowUpdate; - - D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS dxrFlags = getDxrBuildFlags(mRtFlags); - RenderContext* pContext = gpDevice->getRenderContext(); - std::vector instanceDesc = createInstanceDesc(this, hitProgCount); - - // todo: improve this check - make sure things have not changed much and update was enabled last time - bool isRefitPossible = mRefit && mpTopLevelAS && (mInstanceCount == (uint32_t)instanceDesc.size()); - - mInstanceCount = (uint32_t)instanceDesc.size(); - - // Create the top-level acceleration buffers - D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS inputs = {}; - inputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; - inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; - inputs.NumDescs = mInstanceCount; - inputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_ALLOW_UPDATE; - - D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO info; - GET_COM_INTERFACE(gpDevice->getApiHandle(), ID3D12Device5, pDevice5); - pDevice5->GetRaytracingAccelerationStructurePrebuildInfo(&inputs, &info); - - Buffer::SharedPtr pScratchBuffer = Buffer::create(align_to(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, info.ScratchDataSizeInBytes), Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); - - if (!isRefitPossible) - { - mpTopLevelAS = Buffer::create(align_to(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, info.ResultDataMaxSizeInBytes), Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); - } - else - { - pContext->uavBarrier(mpTopLevelAS.get()); - } - - Buffer::SharedPtr pInstanceData = Buffer::create(mInstanceCount * sizeof(D3D12_RAYTRACING_INSTANCE_DESC), Buffer::BindFlags::None, Buffer::CpuAccess::None, instanceDesc.data()); - assert((mInstanceCount != 0) && pInstanceData->getApiHandle() && mpTopLevelAS->getApiHandle() && pScratchBuffer->getApiHandle()); - - // Create the TLAS - D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC asDesc = {}; - asDesc.Inputs = inputs; - asDesc.Inputs.InstanceDescs = pInstanceData->getGpuAddress(); - asDesc.DestAccelerationStructureData = mpTopLevelAS->getGpuAddress(); - asDesc.ScratchAccelerationStructureData = pScratchBuffer->getGpuAddress(); - - if (isRefitPossible) - { - asDesc.Inputs.Flags |= D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PERFORM_UPDATE; - asDesc.SourceAccelerationStructureData = asDesc.DestAccelerationStructureData; - } - - GET_COM_INTERFACE(pContext->getLowLevelData()->getCommandList(), ID3D12GraphicsCommandList4, pList4); - pContext->resourceBarrier(pInstanceData.get(), Resource::State::NonPixelShader); - pList4->BuildRaytracingAccelerationStructure(&asDesc, 0, nullptr); - pContext->uavBarrier(mpTopLevelAS.get()); - - // Create the SRV - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - srvDesc.RaytracingAccelerationStructure.Location = mpTopLevelAS->getGpuAddress(); - - DescriptorSet::Layout layout; - layout.addRange(DescriptorSet::Type::TextureSrv, 0, 1); - DescriptorSet::SharedPtr pSet = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); - assert(pSet); - gpDevice->getApiHandle()->CreateShaderResourceView(nullptr, &srvDesc, pSet->getCpuHandle(0)); - - ResourceWeakPtr pWeak = mpTopLevelAS; - mTlasSrv = std::make_shared(pWeak, pSet, 0, 1, 0, 1); - - mRefit = false; - } -} \ No newline at end of file diff --git a/Framework/Source/Experimental/Raytracing/RtScene.h b/Framework/Source/Experimental/Raytracing/RtScene.h deleted file mode 100644 index 9fc939be0..000000000 --- a/Framework/Source/Experimental/Raytracing/RtScene.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Graphics/Scene/Scene.h" -#include "RtModel.h" -#include - -namespace Falcor -{ - class RtScene : public Scene, inherit_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - SharedPtr shared_from_this() { return inherit_shared_from_this::shared_from_this(); } - - static RtScene::SharedPtr loadFromFile(const std::string& filename, RtBuildFlags rtFlags = RtBuildFlags::None, Model::LoadFlags modelLoadFlags = Model::LoadFlags::None, Scene::LoadFlags sceneLoadFlags = LoadFlags::None); - static RtScene::SharedPtr create(RtBuildFlags rtFlags); - static RtScene::SharedPtr createFromModel(RtModel::SharedPtr pModel); - - ShaderResourceView::SharedPtr getTlasSrv(uint32_t hitProgCount) { createTlas(hitProgCount); return mTlasSrv; } - void addModelInstance(const ModelInstance::SharedPtr& pInstance) override; - using Scene::addModelInstance; - uint32_t getGeometryCount(uint32_t rayCount) { createTlas(rayCount); return mGeometryCount; } - uint32_t getInstanceCount(uint32_t rayCount) { createTlas(rayCount); return mInstanceCount; } - uint32_t getInstanceId(uint32_t model, uint32_t modelInstance, uint32_t mesh, uint32_t meshInstance) const - { - assert(model < mModelInstanceData.size() && mesh < mModelInstanceData[model].meshBase.size()); - uint32_t modelBase = mModelInstanceData[model].modelBase + mModelInstanceData[model].meshInstancesPerModelInstance * modelInstance; - modelBase += mModelInstanceData[model].meshBase[mesh] + meshInstance; - assert(modelBase < mGeometryCount); - return modelBase; - } - virtual bool update(double currentTime, CameraController* cameraController = nullptr) override; - - void setRefit(bool enableRefit) { mEnableRefit = enableRefit; } - - protected: - RtScene(RtBuildFlags rtFlags) : mRtFlags(rtFlags), mpSkinningCache(SkinningCache::create()) {} - uint32_t mTlasHitProgCount = -1; - RtBuildFlags mRtFlags; - - Buffer::SharedPtr mpTopLevelAS; // The top-level acceleration structure for the model - ShaderResourceView::SharedPtr mTlasSrv; - void createTlas(uint32_t rayCount); - std::vector createInstanceDesc(const RtScene* pScene, uint32_t hitProgCount); - - uint32_t mGeometryCount = 0; // The total number of geometries in the scene - uint32_t mInstanceCount = 0; // The total number of TLAS instances in the scene - - struct ModelInstanceData - { - uint32_t modelBase = 0; - uint32_t meshInstancesPerModelInstance = 0; - std::vector meshBase; - }; - - std::vector mModelInstanceData; - std::unordered_map mModelToRtModel; - std::unordered_map mModelInstanceToRtModelInstance; - - SkinningCache::SharedPtr mpSkinningCache; - - bool mEnableRefit = false; - bool mRefit = false; - }; -} diff --git a/Framework/Source/Experimental/Raytracing/RtSceneRenderer.cpp b/Framework/Source/Experimental/Raytracing/RtSceneRenderer.cpp deleted file mode 100644 index 0cfcb0cd5..000000000 --- a/Framework/Source/Experimental/Raytracing/RtSceneRenderer.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "RtSceneRenderer.h" -#include "RtProgramVars.h" -#include "RtState.h" - -namespace Falcor -{ - RtSceneRenderer::SharedPtr RtSceneRenderer::create(RtScene::SharedPtr pScene) - { - return SharedPtr(new RtSceneRenderer(pScene)); - } - - void RtSceneRenderer::setHitShaderData(RtProgramVars* pRtVars, InstanceData& data) - { - const RtScene* pScene = dynamic_cast(mpScene.get()); - uint32_t instanceId = pScene->getInstanceId(data.model, data.modelInstance, data.mesh, data.meshInstance); - data.currentData.pVars = pRtVars->getHitVars(data.progId)[instanceId].get(); - if(data.currentData.pVars) - { - const Model* pModel = mpScene->getModel(data.model).get(); - const Scene::ModelInstance* pModelInstance = mpScene->getModelInstance(data.model, data.modelInstance).get(); - const Mesh* pMesh = pModel->getMesh(data.mesh).get(); - const Model::MeshInstance* pMeshInstance = pModel->getMeshInstance(data.mesh, data.meshInstance).get(); - - setPerFrameData(pRtVars, data); - setPerModelData(data.currentData); - setPerModelInstanceData(data.currentData, pModelInstance, data.modelInstance); - setPerMeshData(data.currentData, pMesh); - setPerMeshInstanceData(data.currentData, pModelInstance, pMeshInstance, 0); - setPerMaterialData(data.currentData, pMesh->getMaterial().get()); - } - } - - void RtSceneRenderer::initializeMeshBufferLocation(const ProgramReflection* pReflection) - { - mMeshBufferLocations.indices = pReflection->getDefaultParameterBlock()->getResourceBinding("gIndices"); - mMeshBufferLocations.texC = pReflection->getDefaultParameterBlock()->getResourceBinding("gTexCrds"); - mMeshBufferLocations.lightmapUVs = pReflection->getDefaultParameterBlock()->getResourceBinding("gLightMapUVs"); - mMeshBufferLocations.normal = pReflection->getDefaultParameterBlock()->getResourceBinding("gNormals"); - mMeshBufferLocations.position = pReflection->getDefaultParameterBlock()->getResourceBinding("gPositions"); - mMeshBufferLocations.prevPosition = pReflection->getDefaultParameterBlock()->getResourceBinding("gPrevPositions"); - mMeshBufferLocations.bitangent = pReflection->getDefaultParameterBlock()->getResourceBinding("gBitangents"); - } - - static bool setVertexBuffer(ParameterBlockReflection::BindLocation bindLocation, uint32_t vertexLoc, const Vao* pVao, GraphicsVars* pVars) - { - if (bindLocation.setIndex != ProgramReflection::kInvalidLocation) - { - const auto& elemDesc = pVao->getElementIndexByLocation(vertexLoc); - if (elemDesc.elementIndex == Vao::ElementDesc::kInvalidIndex) - { - pVars->getDefaultBlock()->setSrv(bindLocation, 0, nullptr); - } - else - { - assert(elemDesc.elementIndex == 0); - pVars->getDefaultBlock()->setSrv(bindLocation, 0, pVao->getVertexBuffer(elemDesc.vbIndex)->getSRV()); - return true; - } - } - return false; - } - - bool RtSceneRenderer::setPerMeshInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance, uint32_t drawInstanceID) - { - const Mesh* pMesh = pMeshInstance->getObject().get(); - const Vao* pVao = pModelInstance->getObject()->getMeshVao(pMesh).get(); - auto& pVars = currentData.pVars; - - if (mMeshBufferLocations.indices.setIndex != ProgramReflection::kInvalidLocation) - { - auto pSrv = pVao->getIndexBuffer() ? pVao->getIndexBuffer()->getSRV() : nullptr; - pVars->getDefaultBlock()->setSrv(mMeshBufferLocations.indices, 0, pSrv); - } - - setVertexBuffer(mMeshBufferLocations.lightmapUVs, VERTEX_LIGHTMAP_UV_LOC, pVao, pVars); - setVertexBuffer(mMeshBufferLocations.texC, VERTEX_TEXCOORD_LOC, pVao, pVars); - setVertexBuffer(mMeshBufferLocations.normal, VERTEX_NORMAL_LOC, pVao, pVars); - setVertexBuffer(mMeshBufferLocations.position, VERTEX_POSITION_LOC, pVao, pVars); - setVertexBuffer(mMeshBufferLocations.bitangent, VERTEX_BITANGENT_LOC, pVao, pVars); - - // Bind vertex buffer for previous positions if it exists. If not, we bind the current positions. - if (!setVertexBuffer(mMeshBufferLocations.prevPosition, VERTEX_PREV_POSITION_LOC, pVao, pVars)) - { - setVertexBuffer(mMeshBufferLocations.prevPosition, VERTEX_POSITION_LOC, pVao, pVars); - } - - return SceneRenderer::setPerMeshInstanceData(currentData, pModelInstance, pMeshInstance, drawInstanceID); - } - - void RtSceneRenderer::setPerFrameData(RtProgramVars* pRtVars, InstanceData& data) - { - SceneRenderer::setPerFrameData(data.currentData); - } - - void RtSceneRenderer::setRayGenShaderData(RtProgramVars* pRtVars, InstanceData& data) - { - data.currentData.pVars = pRtVars->getRayGenVars().get(); - setPerFrameData(pRtVars, data); - } - - void RtSceneRenderer::setGlobalData(RtProgramVars* pRtVars, InstanceData& data) - { - data.currentData.pVars = pRtVars->getGlobalVars().get(); - - GraphicsVars* pVars = data.currentData.pVars; - ParameterBlockReflection::BindLocation loc = pVars->getReflection()->getDefaultParameterBlock()->getResourceBinding("gRtScene"); - if (loc.setIndex != ProgramReflection::kInvalidLocation) - { - RtScene* pRtScene = dynamic_cast(mpScene.get()); - pVars->getDefaultBlock()->setSrv(loc, 0, pRtScene->getTlasSrv(pRtVars->getHitProgramsCount())); - } - - ConstantBuffer::SharedPtr pDxrPerFrame = pVars->getConstantBuffer("DxrPerFrame"); - if (pDxrPerFrame) - { - pDxrPerFrame["hitProgramCount"] = pRtVars->getHitProgramsCount(); - } - SceneRenderer::setPerFrameData(data.currentData); - } - - void RtSceneRenderer::setMissShaderData(RtProgramVars* pRtVars, InstanceData& data) - { - data.currentData.pVars = pRtVars->getMissVars(data.progId).get(); - if(data.currentData.pVars) - { - setPerFrameData(pRtVars, data); - } - } - - void RtSceneRenderer::renderScene(RenderContext* pContext, RtProgramVars::SharedPtr pRtVars, RtState::SharedPtr pState, uvec2 targetDim, Camera* pCamera) - { - renderScene(pContext, pRtVars, pState, uvec3(targetDim, 1), pCamera); - } - - void RtSceneRenderer::renderScene(RenderContext* pContext, RtProgramVars::SharedPtr pRtVars, RtState::SharedPtr pState, uvec3 targetDim, Camera* pCamera) - { - InstanceData data; - data.currentData.pCamera = pCamera == nullptr ? mpScene->getActiveCamera().get() : pCamera; - uint32_t hitCount = pRtVars->getHitProgramsCount(); - if (hitCount) - { - updateVariableOffsets(pState->getProgram()->getHitProgram(0)->getReflector().get()); // Using the local+global reflector, some resources are `shared` - initializeMeshBufferLocation(pState->getProgram()->getHitProgram(0)->getLocalReflector().get()); // Using the local reflector only - } - - setRayGenShaderData(pRtVars.get(), data); - setGlobalData(pRtVars.get(), data); - - // Set the miss-shader data - for (data.progId = 0; data.progId < pRtVars->getMissProgramsCount(); data.progId++) - { - if(pRtVars->getMissVars(data.progId)) - { - setMissShaderData(pRtVars.get(), data); - } - } - - // Set the hit-shader data - for(data.progId = 0 ; data.progId < hitCount ; data.progId++) - { - if(pRtVars->getHitVars(data.progId).empty()) continue; - for (data.model = 0; data.model < mpScene->getModelCount(); data.model++) - { - const Model* pModel = mpScene->getModel(data.model).get(); - data.currentData.pModel = pModel; - for (data.modelInstance = 0; data.modelInstance < mpScene->getModelInstanceCount(data.model); data.modelInstance++) - { - for (data.mesh = 0; data.mesh < pModel->getMeshCount(); data.mesh++) - { - const Mesh* pMesh = pModel->getMesh(data.mesh).get(); - for (data.meshInstance = 0; data.meshInstance < pModel->getMeshInstanceCount(data.mesh); data.meshInstance++) - { - setHitShaderData(pRtVars.get(), data); - } - } - } - } - } - - if (!pRtVars->apply(pContext, pState->getRtso().get())) - { - logError("RtSceneRenderer::renderScene() - applying RtProgramVars failed, most likely because we ran out of descriptors.", true); - assert(false); - } - pContext->raytrace(pRtVars, pState, targetDim.x, targetDim.y, targetDim.z); - } -} diff --git a/Framework/Source/Experimental/Raytracing/RtSceneRenderer.h b/Framework/Source/Experimental/Raytracing/RtSceneRenderer.h deleted file mode 100644 index 4af31594b..000000000 --- a/Framework/Source/Experimental/Raytracing/RtSceneRenderer.h +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "RtScene.h" -#include "Graphics/Scene/SceneRenderer.h" - -namespace Falcor -{ - class RtProgramVars; - class RtState; - - class RtSceneRenderer : public SceneRenderer, inherit_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - static SharedPtr create(RtScene::SharedPtr pScene); - - deprecate("3.3", "Ray dispatch now accepts depth as a parameter. Using the deprecated version will assume depth = 1.") - void renderScene(RenderContext* pContext, std::shared_ptr pRtVars, std::shared_ptr pState, uvec2 targetDim, Camera* pCamera = nullptr); - void renderScene(RenderContext* pContext, std::shared_ptr pRtVars, std::shared_ptr pState, uvec3 targetDim, Camera* pCamera = nullptr); - protected: - RtSceneRenderer(RtScene::SharedPtr pScene) : SceneRenderer(pScene) {} - struct InstanceData - { - CurrentWorkingData currentData; - uint32_t model; - uint32_t modelInstance; - uint32_t mesh; - uint32_t meshInstance; - uint32_t progId; - }; - - virtual void setPerFrameData(RtProgramVars* pRtVars, InstanceData& data); - virtual bool setPerMeshInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance, uint32_t drawInstanceID) override; - virtual void setHitShaderData(RtProgramVars* pRtVars, InstanceData& data); - virtual void setMissShaderData(RtProgramVars* pRtVars, InstanceData& data); - virtual void setRayGenShaderData(RtProgramVars* pRtVars, InstanceData& data); - virtual void setGlobalData(RtProgramVars* pRtVars, InstanceData& data); - - void initializeMeshBufferLocation(const ProgramReflection* pReflection); - - struct MeshBufferLocations - { - ParameterBlockReflection::BindLocation indices; - ParameterBlockReflection::BindLocation normal; - ParameterBlockReflection::BindLocation bitangent; - ParameterBlockReflection::BindLocation position; - ParameterBlockReflection::BindLocation prevPosition; - ParameterBlockReflection::BindLocation texC; - ParameterBlockReflection::BindLocation lightmapUVs; - }; - MeshBufferLocations mMeshBufferLocations; - }; -} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.cpp b/Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.cpp deleted file mode 100644 index dd6010f17..000000000 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "RenderGraphImportExport.h" -#include "RenderGraphScripting.h" -#include "RenderPassLibrary.h" -#include "RenderGraphIR.h" -#include "Graphics/Scene/Scene.h" -#include - -namespace Falcor -{ - static void updateGraphStrings(const std::string& graph, std::string& file, std::string& func) - { - file = file.empty() ? graph + ".py" : file; - func = func.empty() ? "render_graph_" + graph : func; - } - - RenderGraph::SharedPtr RenderGraphImporter::import(std::string graphName, std::string filename, std::string funcName, const Fbo* pDstFbo) - { - bool gotFuncName = funcName.size(); - updateGraphStrings(graphName, filename, funcName); - - std::string fullpath; - if (findFileInDataDirectories(filename, fullpath) == false) - { - logError("Error when loading graph. Can't find the file `" + filename + "`"); - return nullptr; - } - - // Run the script and try to get the graph - RenderGraphScripting::SharedPtr pScripting = RenderGraphScripting::create(fullpath); - RenderGraph::SharedPtr pGraph; - if(gotFuncName) pGraph = pScripting->getGraph(graphName); - - if(pGraph == nullptr) - { - // If we didn't succeed or got a custom function name, try and call the graph function explicitly - if (pScripting->runScript(graphName + '=' + funcName + "()") == false) return nullptr; - pGraph = pScripting->getGraph(graphName); - } - - if (!pGraph) logError("Can't import graphs from file `" + filename + "`"); - if (pGraph && pDstFbo) pGraph->onResize(pDstFbo); - - return pGraph; - } - - std::vector RenderGraphImporter::importAllGraphs(const std::string& filename, const Fbo* pDstFbo) - { - RenderGraphScripting::SharedPtr pScripting = RenderGraphScripting::create(filename); - if (!pScripting) return {}; - - const auto& scriptVec = pScripting->getGraphs(); - std::vector res; - res.reserve(scriptVec.size()); - - for (const auto& s : scriptVec) - { - if(pDstFbo) s.obj->onResize(pDstFbo); - res.push_back({ s.name, s.obj }); - } - - return res; - } - - bool RenderGraphExporter::save(const std::shared_ptr& pGraph, std::string graphName, std::string filename, std::string funcName, ExportFlags exportFlags) - { - updateGraphStrings(graphName, filename, funcName); - RenderGraphIR::SharedPtr pIR = RenderGraphIR::create(graphName); - - // Register passes that are loaded from dlls - auto libNames = RenderPassLibrary::enumerateLibraries(); - for (const auto& libName : libNames) - { - pIR->loadPassLibrary(getFilenameFromPath(libName)); - } - - //Add the passes - for (const auto& node : pGraph->mNodeData) - { - const auto& data = node.second; - pIR->addPass(data.pPass->getName(), data.nodeName, data.pPass->getScriptingDictionary()); - } - - // Add the edges - for (const auto& edge : pGraph->mEdgeData) - { - const auto& data = edge.second; - const auto& srcPass = pGraph->mNodeData[pGraph->mpGraph->getEdge(edge.first)->getSourceNode()].nodeName; - const auto& dstPass = pGraph->mNodeData[pGraph->mpGraph->getEdge(edge.first)->getDestNode()].nodeName; - std::string src = srcPass + (data.srcField.size() ? '.' + data.srcField : data.srcField); - std::string dst = dstPass + (data.dstField.size() ? '.' + data.dstField : data.dstField); - pIR->addEdge(src, dst); - } - - // Graph outputs - for (const auto& out : pGraph->mOutputs) - { - std::string str = pGraph->mNodeData[out.nodeId].nodeName + '.' + out.field; - pIR->markOutput(str); - } - - // if set, add the scene - if (exportFlags == ExportFlags::SetScene) - { - auto pScene = pGraph->getScene(); - if (pScene != nullptr) { pIR->setScene(pScene.get()); } - } - - // Save it to file - std::ofstream f(filename); - f << pIR->getIR() << std::endl; - f << graphName << " = " << funcName + "()"; - return true; - } -} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphScripting.cpp b/Framework/Source/Experimental/RenderGraph/RenderGraphScripting.cpp deleted file mode 100644 index 44e303ab9..000000000 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphScripting.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "RenderGraphScripting.h" -#include "Utils/Scripting/Scripting.h" -#include -#include -#include "Experimental/RenderGraph/RenderPassLibrary.h" -#include "pybind11/operators.h" -#include "Graphics/Scene/Scene.h" - -using namespace pybind11::literals; - -namespace Falcor -{ - const char* RenderGraphScripting::kAddPass = "addPass"; - const char* RenderGraphScripting::kRemovePass = "removePass"; - const char* RenderGraphScripting::kAddEdge = "addEdge"; - const char* RenderGraphScripting::kRemoveEdge = "removeEdge"; - const char* RenderGraphScripting::kMarkOutput = "markOutput"; - const char* RenderGraphScripting::kUnmarkOutput = "unmarkOutput"; - const char* RenderGraphScripting::kAutoGenEdges = "autoGenEdges"; - const char* RenderGraphScripting::kCreateGraph = "createRenderGraph"; - const char* RenderGraphScripting::kCreatePass = "createRenderPass"; - const char* RenderGraphScripting::kUpdatePass = "updatePass"; - const char* RenderGraphScripting::kLoadPassLibrary = "loadRenderPassLibrary"; - const char* RenderGraphScripting::kSetName = "setName"; - const char* RenderGraphScripting::kSetScene = "setScene"; - - void RenderGraphScripting::registerScriptingObjects(pybind11::module& m) - { - -#define pf(a) value(#a, RenderGraph::PassFlags::a) - // RenderGraph::PassFlags - pybind11::enum_(m, "PassFlags").pf(None).pf(ForceExecution); -#undef pf - - // RenderGraph - m.def(kCreateGraph, &RenderGraph::create, "name"_a = ""); - - void(RenderGraph::*renderGraphRemoveEdge)(const std::string&, const std::string&)(&RenderGraph::removeEdge); - auto graphClass = pybind11::class_(m, "Graph"); - graphClass.def(kAddPass, &RenderGraph::addPass, "renderPass"_a, "passName"_a, "passFlags"_a = RenderGraph::PassFlags::None).def(kRemovePass, &RenderGraph::removePass); - graphClass.def(kAddEdge, &RenderGraph::addEdge).def(kRemoveEdge, renderGraphRemoveEdge); - graphClass.def(kMarkOutput, &RenderGraph::markOutput).def(kUnmarkOutput, &RenderGraph::unmarkOutput); - graphClass.def(kAutoGenEdges, &RenderGraph::autoGenEdges); - graphClass.def(kSetName, &RenderGraph::setName); - graphClass.def(kSetScene, &RenderGraph::setScene); - - // RenderPass - pybind11::class_(m, "RenderPass"); - - // RenderPassLibrary - const auto& createRenderPass = [](const std::string& passName, pybind11::dict d = {})->RenderPass::SharedPtr - { - return RenderPassLibrary::instance().createPass(passName.c_str(), Dictionary(d)); - }; - m.def(kCreatePass, createRenderPass, "passName"_a, "dict"_a = pybind11::dict()); - - const auto& loadPassLibrary = [](const std::string& library) - { - return RenderPassLibrary::instance().loadLibrary(library); - }; - m.def(kLoadPassLibrary, loadPassLibrary); - - const auto& updateRenderPass = [](const RenderGraph::SharedPtr& pGraph, const std::string& passName, pybind11::dict d ) - { - pGraph->updatePass(passName, Dictionary(d)); - }; - graphClass.def(kUpdatePass, updateRenderPass); - } - - RenderGraphScripting::SharedPtr RenderGraphScripting::create() - { - return SharedPtr(new RenderGraphScripting()); - } - - RenderGraphScripting::SharedPtr RenderGraphScripting::create(const std::string& filename) - { - SharedPtr pThis = create(); - - if (findFileInDataDirectories(filename, pThis->mFilename) == false) - { - logError("Error when opening render graphs script file. Can't find the file `" + filename + "`"); - return nullptr; - } - - pThis->runScript(readFile(pThis->mFilename)); - return pThis; - } - - bool RenderGraphScripting::runScript(const std::string& script) - { - std::string log; - if (Scripting::runScript(script, log, mContext) == false) - { - logError("Can't run render-graphs script.\n" + log); - return false; - } - - mGraphVec = mContext.getObjects(); - return true; - } - - void RenderGraphScripting::addGraph(const std::string& name, const RenderGraph::SharedPtr& pGraph) - { - try - { - mContext.getObject(name); - logWarning("RenderGraph `" + name + "` already exists. Replacing the current object"); - } - catch (std::exception) {} - mContext.setObject(name, pGraph); - } - - RenderGraph::SharedPtr RenderGraphScripting::getGraph(const std::string& name) const - { - try - { - return mContext.getObject(name); - } - catch (std::exception) - { - logWarning("Can't find RenderGraph `" + name + "` in RenderGraphScriptContext"); - return nullptr; - } - } -} diff --git a/Framework/Source/Experimental/RenderGraph/RenderPassReflection.cpp b/Framework/Source/Experimental/RenderGraph/RenderPassReflection.cpp deleted file mode 100644 index dc4da834e..000000000 --- a/Framework/Source/Experimental/RenderGraph/RenderPassReflection.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "RenderPassReflection.h" - -namespace Falcor -{ - RenderPassReflection::Field::Field(const std::string& name, const std::string& desc, Visibility v) : mName(name), mVisibility(v), mDesc(desc) - { - } - - RenderPassReflection::Field& RenderPassReflection::Field::texture1D(uint32_t width) - { - mType = Type::Texture1D; - mWidth = width; - mHeight = 1; - mDepth = 1; - return *this; - } - - RenderPassReflection::Field& RenderPassReflection::Field::texture2D(uint32_t width, uint32_t height, uint32_t sampleCount) - { - mType = Type::Texture2D; - mWidth = width; - mHeight = height; - mSampleCount = sampleCount; - mDepth = 1; - return *this; - } - - RenderPassReflection::Field& RenderPassReflection::Field::texture3D(uint32_t width, uint32_t height, uint32_t depth) - { - mType = Type::Texture3D; - mWidth = width; - mHeight = height; - mDepth = depth; - return *this; - } - - RenderPassReflection::Field& RenderPassReflection::Field::textureCube(uint32_t width, uint32_t height) - { - mType = Type::TextureCube; - mWidth = width; - mHeight = height; - mDepth = 1; - return *this; - } - - RenderPassReflection::Field& RenderPassReflection::Field::arraySize(uint32_t a) { mArraySize = a; return *this; } - RenderPassReflection::Field& RenderPassReflection::Field::mipLevels(uint32_t m) { mMipLevels = m; return *this; } - RenderPassReflection::Field& RenderPassReflection::Field::format(ResourceFormat f) { mFormat = f; return *this; } - RenderPassReflection::Field& RenderPassReflection::Field::bindFlags(Resource::BindFlags flags) { mBindFlags = flags; return *this; } - RenderPassReflection::Field& RenderPassReflection::Field::flags(Flags flags) { mFlags = flags; return *this; } - RenderPassReflection::Field& RenderPassReflection::Field::visibility(Visibility vis) { mVisibility = vis; return *this; } - - bool RenderPassReflection::Field::isValid() const - { - if (mType == Type::Texture3D && mArraySize > 1) - { - logWarning("Trying to create a Texture3D RenderPassReflection::Field `" + mName + "` with array-size larger than 1. This is illegal."); - return false; - } - - if (mSampleCount > 1 && mMipLevels > 1) - { - logWarning("Trying to create a multisampled RenderPassReflection::Field `" + mName + "` with mip-count larger than 1. This is illegal."); - return false; - } - - return true; - } - - RenderPassReflection::Field& RenderPassReflection::addField(const std::string& name, const std::string& desc, Field::Visibility visibility) - { - // See if the field already exists - for (auto& f : mFields) - { - if (f.getName() == name) - { - // We can only merge input and output fields, otherwise override the previous field - bool ioField = is_set(f.getVisibility(), Field::Visibility::Input | Field::Visibility::Output); - bool ioRequest = is_set(visibility, Field::Visibility::Input | Field::Visibility::Output); - if (ioField && ioRequest) - { - f.mVisibility |= visibility; - } - else if((f.getVisibility() & visibility) != visibility) - { - logError("Trying to add an existing field `" + name + "` to RenderPassReflection, but the visibility flags mismatch. Overriding the previous definition"); - } - return f; - } - } - - mFields.push_back(Field(name, desc, visibility)); - return mFields.back(); - } - - RenderPassReflection::Field& RenderPassReflection::addInput(const std::string& name, const std::string& desc) - { - return addField(name, desc, Field::Visibility::Input); - } - - RenderPassReflection::Field& RenderPassReflection::addOutput(const std::string& name, const std::string& desc) - { - return addField(name, desc, Field::Visibility::Output); - } - - RenderPassReflection::Field& RenderPassReflection::addInputOutput(const std::string& name, const std::string& desc) - { - return addField(name, desc, Field::Visibility::Input | Field::Visibility::Output); - } - - RenderPassReflection::Field& RenderPassReflection::addInternal(const std::string& name, const std::string& desc) - { - return addField(name, desc, Field::Visibility::Internal); - } - - const RenderPassReflection::Field& RenderPassReflection::getField(const std::string& name) const - { - for (const auto& field : mFields) - { - if (field.getName() == name) return field; - } - std::string error = "Can't find a field named `" + name + "` in RenderPassReflection"; - throw std::runtime_error(error.c_str()); - } - -} \ No newline at end of file diff --git a/Framework/Source/Experimental/RenderGraph/ResourceCache.cpp b/Framework/Source/Experimental/RenderGraph/ResourceCache.cpp deleted file mode 100644 index ad750fbf5..000000000 --- a/Framework/Source/Experimental/RenderGraph/ResourceCache.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "ResourceCache.h" - -namespace Falcor -{ - ResourceCache::SharedPtr ResourceCache::create() - { - return SharedPtr(new ResourceCache()); - } - - void ResourceCache::reset() - { - mNameToIndex.clear(); - mResourceData.clear(); - } - - const std::shared_ptr& ResourceCache::getResource(const std::string& name) const - { - static const std::shared_ptr pNull; - auto extIt = mExternalInputs.find(name); - - // Search external resources if not found in render graph resources - if (extIt == mExternalInputs.end()) - { - const auto& it = mNameToIndex.find(name); - if (it == mNameToIndex.end()) - { - return pNull; - } - - return mResourceData[it->second].pResource; - } - - return extIt->second; - } - - const RenderPassReflection::Field& ResourceCache::getResourceReflection(const std::string& name) const - { - uint32_t i = mNameToIndex.at(name); - return mResourceData[i].field; - } - - void ResourceCache::registerExternalInput(const std::string& name, const std::shared_ptr& pResource) - { - mExternalInputs[name] = pResource; - } - - void ResourceCache::removeExternalInput(const std::string& name) - { - auto it = mExternalInputs.find(name); - if (it == mExternalInputs.end()) - { - logWarning("ResourceCache::removeExternalResource: " + name + " does not exist."); - return; - } - - mExternalInputs.erase(it); - } - - static ResourceBindFlags getBindFlagsFromFormat(Resource::BindFlags flags, ResourceFormat format, RenderPassReflection::Field::Visibility vis) - { - auto mask = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; - if (is_set(vis, RenderPassReflection::Field::Visibility::Output)) mask |= Resource::BindFlags::DepthStencil | Resource::BindFlags::RenderTarget; - - auto formatFlags = getFormatBindFlags(format); - formatFlags = formatFlags & mask; - return flags | formatFlags; - } - - /** Overwrite previously unknown/unspecified fields with specified ones. - - If a field property is specified both in the existing cache, as well as the input properties, - a warning will be logged and the cached properties will not be changed. - */ - bool mergeFields(RenderPassReflection::Field& base, const RenderPassReflection::Field& newField, const std::string& newFieldName) - { - auto warn = [&](const std::string& msg) -> bool - { - const std::string warningMsg = "Can't merge RenderPassReflection::Fields. base(" + base.getName() + "), newField(" + newField.getName() + "). "; - logWarning(warningMsg + msg); - return false; - }; - - if (base.getType() != newField.getType()) return warn("mismatching types"); - - // Default to base dimension - // If newField property is not 0, retrieve value from newField - // If both newField and base property is specified, generate warning. -#define get_dim(var, dim) \ - var = base.get##dim(); \ - if (newField.get##dim() != 0) { \ - if (base.get##dim() == 0) var = newField.get##dim(); \ - else if(base.get##dim() != newField.get##dim()) return warn(std::string(#dim) + " already specified. "); } - - uint32_t w = 0, h = 0, d = 0; - get_dim(w, Width); - get_dim(h, Height); - get_dim(d, Depth); -#undef get_dim - - // Merge sample counts - uint32_t sampleCount = base.getSampleCount(); - if (newField.getSampleCount() != 0) - { - // if base field doesn't have anything specified, apply new property - if (base.getSampleCount() == 0) - { - sampleCount = newField.getSampleCount(); - } - // if they differ in any other way, that is invalid - else if (base.getSampleCount() != newField.getSampleCount()) - { - return warn("Cannot merge sample count. Base has " + std::to_string(base.getSampleCount()) + ", new-field has " + std::to_string(newField.getSampleCount())); - } - } - - switch (base.getType()) - { - case RenderPassReflection::Field::Type::Texture1D: - base.texture1D(w); - break; - case RenderPassReflection::Field::Type::Texture2D: - base.texture2D(w, h, sampleCount); - break; - case RenderPassReflection::Field::Type::Texture3D: - base.texture3D(w, h, d); - break; - case RenderPassReflection::Field::Type::TextureCube: - base.textureCube(w, h); - default: - should_not_get_here(); - break; - } - - // merge array-size - if (newField.getArraySize() != 0) - { - if (base.getArraySize() == 0) base.arraySize(newField.getArraySize()); - else if (base.getArraySize() != newField.getArraySize()) return warn("Mismatching array-sizes"); - } - - // merge mip-levels - if (newField.getMipLevels() != 0) - { - if (base.getMipLevels() == 0) base.mipLevels(newField.getMipLevels()); - else if (base.getMipLevels() != newField.getMipLevels()) return warn("Mismatching mip-levels"); - } - - // Format - if (newField.getFormat() != ResourceFormat::Unknown) - { - if (base.getFormat() == ResourceFormat::Unknown) base.format(newField.getFormat()); - else if (base.getFormat() != newField.getFormat()) return warn("Format already specified"); - } - - // Visibility - assert(is_set(newField.getVisibility(), RenderPassReflection::Field::Visibility::Internal) == false); // We can't alias/merge internal fields - assert(is_set(base.getVisibility(), RenderPassReflection::Field::Visibility::Internal) == false); // We can't alias/merge internal fields - base.visibility(base.getVisibility() | newField.getVisibility()); - - Resource::BindFlags baseFlags = base.getBindFlags(); - Resource::BindFlags newFlags = newField.getBindFlags(); - - if ((is_set(baseFlags, Resource::BindFlags::RenderTarget) && is_set(newFlags, Resource::BindFlags::DepthStencil)) || - (is_set(baseFlags, Resource::BindFlags::DepthStencil) && is_set(newFlags, Resource::BindFlags::RenderTarget))) - { - return warn("Usage contained both RenderTarget and DepthStencil bind flags"); - } - else - { - base.bindFlags(baseFlags | newFlags); - } - - return true; - } - - void mergeTimePoint(uint32_t& minTime, uint32_t& maxTime, uint32_t newTime) - { - minTime = min(minTime, newTime); - maxTime = max(maxTime, newTime); - } - - void ResourceCache::registerField(const std::string& name, RenderPassReflection::Field field, uint32_t timePoint, const std::string& alias) - { - auto nameIt = mNameToIndex.find(name); - auto aliasIt = mNameToIndex.find(alias); - - // If two fields were registered separately before, but are now aliased together, merge the fields, with alias field being the base - if ((nameIt != mNameToIndex.end()) && (aliasIt != mNameToIndex.end()) && (nameIt->second != aliasIt->second)) - { - // Merge data - auto& baseResData = mResourceData[aliasIt->second]; - auto& newResData = mResourceData[nameIt->second]; - mergeFields(baseResData.field, newResData.field, name); - mergeTimePoint(baseResData.firstUsed, baseResData.lastUsed, newResData.firstUsed); - mergeTimePoint(baseResData.firstUsed, baseResData.lastUsed, newResData.lastUsed); - - // Clear data that has been merged - mResourceData[nameIt->second] = ResourceData(); - - // Redirect 'name' to look up the alias field - nameIt->second = aliasIt->second; - } - - // If name exists, update time range - if (mNameToIndex.count(name) > 0) - { - uint32_t index = mNameToIndex[name]; - mergeTimePoint(mResourceData[index].firstUsed, mResourceData[index].lastUsed, timePoint); - return; - } - - bool addAlias = (alias.empty() == false); - if (addAlias && mNameToIndex.count(alias) == 0) - { - logWarning("ResourceCache::registerField: Field named " + alias + " not found. Cannot add " + name + "as an alias. Creating new entry."); - addAlias = false; - } - - // Add a new field - if (addAlias == false) - { - assert(mNameToIndex.count(name) == 0); - mNameToIndex[name] = (uint32_t)mResourceData.size(); - mResourceData.push_back({ field, true, timePoint, timePoint, nullptr }); - } - // Add alias - else - { - uint32_t index = mNameToIndex[alias]; - mNameToIndex[name] = index; - - mergeFields(mResourceData[index].field, field, name); - mergeTimePoint(mResourceData[index].firstUsed, mResourceData[index].lastUsed, timePoint); - - mResourceData[index].dirty = true; - } - } - - Texture::SharedPtr createTextureForPass(const ResourceCache::DefaultProperties& params, const RenderPassReflection::Field& field) - { - uint32_t width = field.getWidth() ? field.getWidth() : params.width; - uint32_t height = field.getHeight() ? field.getHeight() : params.height; - uint32_t depth = field.getDepth() ? field.getDepth() : 1; - uint32_t sampleCount = field.getSampleCount() ? field.getSampleCount() : 1; - ResourceFormat format = field.getFormat() == ResourceFormat::Unknown ? params.format : field.getFormat(); - - auto bindFlags = getBindFlagsFromFormat(field.getBindFlags(), format, field.getVisibility()); - - Texture::SharedPtr pTexture; - if (depth > 1) - { - assert(sampleCount == 1); - pTexture = Texture::create3D(width, height, depth, format, 1, nullptr, bindFlags); - } - else if (height > 1 || sampleCount > 1) - { - if (sampleCount > 1) - { - pTexture = Texture::create2DMS(width, height, format, sampleCount, 1, bindFlags); - } - else - { - pTexture = Texture::create2D(width, height, format, 1, 1, nullptr, bindFlags); - } - } - else - { - pTexture = Texture::create1D(width, format, 1, 1, nullptr, bindFlags); - } - - return pTexture; - } - - void ResourceCache::allocateResources(const DefaultProperties& params) - { - for (auto& data : mResourceData) - { - if ((data.pResource == nullptr || data.dirty) && data.field.isValid()) - { - data.pResource = createTextureForPass(params, data.field); - data.dirty = false; - } - } - } -} diff --git a/Framework/Source/Experimental/RenderPasses/ImageLoader.cpp b/Framework/Source/Experimental/RenderPasses/ImageLoader.cpp deleted file mode 100644 index 4e17e5a3e..000000000 --- a/Framework/Source/Experimental/RenderPasses/ImageLoader.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "ImageLoader.h" -#include "Graphics/TextureHelper.h" -#include "API/RenderContext.h" -#include "Utils/Gui.h" -#include - -namespace fs = std::experimental::filesystem; - -namespace Falcor -{ - const char* ImageLoader::kDesc = "Load an image into a texture"; - - static const std::string kDst = "dst"; - static const std::string kImage = "fileName"; - static const std::string kMips = "mips"; - static const std::string kSrgb = "srgb"; - static const std::string kDefaultImage = "image"; - - RenderPassReflection ImageLoader::reflect() const - { - RenderPassReflection reflector; - reflector.addOutput(kDst, "Destination texture"); - - return reflector; - } - - ImageLoader::SharedPtr ImageLoader::create(const Dictionary& dict) - { - SharedPtr pPass = SharedPtr(new ImageLoader); - - for (const auto& v : dict) - { - if (v.key() == kImage) - { - pPass->mImageName = v.val().operator std::string(); - } - else if(v.key() == kSrgb) - { - pPass->mLoadSRGB = v.val(); - } - else if (v.key() == kMips) - { - pPass->mGenerateMips = v.val(); - } - else - { - logWarning("Unknown field `" + v.key() + "` in a ImageLoader dictionary"); - } - } - - return pPass; - } - - Dictionary ImageLoader::getScriptingDictionary() const - { - Dictionary dict; - dict[kImage] = mImageName; - dict[kMips] = mGenerateMips; - dict[kSrgb] = mLoadSRGB; - return dict; - } - - ImageLoader::ImageLoader() : RenderPass("ImageLoader") - { - } - - void ImageLoader::execute(RenderContext* pContext, const RenderData* pRenderData) - { - if (!mpTex) - { - // attempt to load default image - auto& dict = pRenderData->getDictionary(); - if (!mImageName.size() && dict.keyExists(kDefaultImage)) - { - std::string defaultImageName = dict[kDefaultImage].operator std::string(); - mImageName = defaultImageName; - } - if (mImageName.size()) - { - if (dict.keyExists(mImageName)) { mpTex = dict[mImageName]; } - else - { - mImageName = stripDataDirectories(mImageName); - mpTex = createTextureFromFile(mImageName, mGenerateMips, mLoadSRGB); - // if updatePass is called, the image will be unloaded - // save the image pointer in the shared dictionary to avoid this - dict[mImageName] = mpTex; - } - } - if (!mpTex) { logWarning("No image loaded! Not able to execute image loader pass."); return; } - } - - const auto& pDstTex = pRenderData->getTexture(kDst); - if (pDstTex) - { - pContext->blit(mpTex->getSRV(), pDstTex->getRTV()); - } - else - { - logWarning("ImageLoader::execute() - missing an input or output resource"); - } - } - - void ImageLoader::renderUI(Gui* pGui, const char* uiGroup) - { - if (!uiGroup || pGui->beginGroup(uiGroup)) - { - bool reloadImage = pGui->addTextBox("Image File", mImageName); ; - reloadImage |= pGui->addCheckBox("Load As SRGB", mLoadSRGB); - reloadImage |= pGui->addCheckBox("Generate Mipmaps", mGenerateMips); - if (pGui->addButton("loadFile")) { reloadImage |= openFileDialog({}, mImageName); } - if (pGui->addButton("clear")) - { - mpTex = nullptr; mImageName.clear(); - } - - if (mpTex) - { - pGui->addImage(mImageName.c_str(), mpTex, { 320, 320 }); - } - - if (reloadImage && mImageName.size()) - { - mImageName = stripDataDirectories(mImageName); - mpTex = createTextureFromFile(mImageName, mGenerateMips, mLoadSRGB); - } - - if (uiGroup) pGui->endGroup(); - } - } -} \ No newline at end of file diff --git a/Framework/Source/Falcor.h b/Framework/Source/Falcor.h deleted file mode 100644 index 2613a87df..000000000 --- a/Framework/Source/Falcor.h +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#ifdef _WIN32 -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#include "FalcorConfig.h" -#include "Framework.h" -#include "Sample.h" -#define _USE_MATH_DEFINES -#include - -// Core -#include "API/BlendState.h" -#include "API/Buffer.h" -#include "API/DepthStencilState.h" -#include "API/Device.h" -#include "API/FBO.h" -#include "API/Formats.h" -#include "API/GpuTimer.h" -#include "API/GraphicsStateObject.h" -#include "API/RasterizerState.h" -#include "API/RenderContext.h" -#include "API/Sampler.h" -#include "API/Shader.h" -#include "API/StructuredBuffer.h" -#include "API/Texture.h" -#include "API/ConstantBuffer.h" -#include "API/VAO.h" -#include "API/VertexLayout.h" -#include "API/Window.h" -#include "API/TypedBuffer.h" -#include "API/CopyContext.h" -#include "API/ComputeContext.h" -#include "API/QueryHeap.h" - -#if defined FALCOR_D3D12 || defined FALCOR_VK -#include "API/DescriptorSet.h" -#include "API/LowLevel/DescriptorPool.h" -#include "API/LowLevel/FencedPool.h" -#include "API/LowLevel/GpuFence.h" -#include "API/LowLevel/RootSignature.h" -#endif //FALCOR_D3D12 || defined FALCOR_VK - -// Graphics -#include "Graphics/Camera/Camera.h" -#include "Graphics/Camera/CameraController.h" -#include "Graphics/GraphicsState.h" -#include "Graphics/FullScreenPass.h" -#include "Graphics/TextureHelper.h" -#include "Graphics/Light.h" -#include "Graphics/LightProbe.h" -#include "Graphics/FboHelper.h" -#include "Graphics/ComputeState.h" - -// Program -#include "Graphics/Program/ProgramReflection.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/Program/ProgramVersion.h" -#include "Graphics/Program/Program.h" -#include "Graphics/Program/GraphicsProgram.h" -#include "Graphics/Program/ComputeProgram.h" -#include "Graphics/Program/ParameterBlock.h" - -// Material -#include "Graphics/Material/Material.h" - -// Model -#include "Graphics/Model/Mesh.h" -#include "Graphics/Model/Model.h" -#include "Graphics/Model/ModelRenderer.h" - -// Scene -#include "Graphics/Scene/Scene.h" -#include "Graphics/Scene/SceneRenderer.h" -#include "Graphics/Scene/Editor/SceneEditor.h" - -// Math -#include "Utils/Math/FalcorMath.h" -#include "Utils/Math/CubicSpline.h" -#include "Utils/Math/ParallelReduction.h" - -// Utils -#include "Utils/Bitmap.h" -#include "Utils/DDSHeader.h" -#include "Utils/Font.h" -#include "Utils/Gui.h" -#include "Utils/Logger.h" -#include "Utils/TextRenderer.h" -#include "Utils/CpuTimer.h" -#include "Utils/UserInput.h" -#include "Utils/Profiler.h" -#include "Utils/StringUtils.h" -#include "Utils/BinaryFileStream.h" -#include "Utils/Video/VideoEncoder.h" -#include "Utils/Video/VideoEncoderUI.h" -#include "Utils/Video/VideoDecoder.h" -#include "Utils/Platform/OS.h" -#include "Utils/Platform/ProgressBar.h" -#include "Utils/ThreadPool.h" -#include "Utils/PatternGenerators/DxSamplePattern.h" -#include "Utils/PatternGenerators/HaltonSamplePattern.h" - -// VR -#include "VR/OpenVR/VRSystem.h" -#include "VR/VrFbo.h" - -// Effects -#include "Effects/NormalMap/LeanMap.h" -#include "Effects/Shadows/CSM.h" -#include "Effects/Utils/GaussianBlur.h" -#include "Effects/SkyBox/SkyBox.h" -#include "Effects/ToneMapping/ToneMapping.h" -#include "Effects/AmbientOcclusion/SSAO.h" -#include "Effects/ParticleSystem/ParticleSystem.h" -#include "Effects/TAA/TAA.h" -#include "Effects/FXAA/FXAA.h" - -#define FALCOR_MAJOR_VERSION 3 -#define FALCOR_MINOR_VERSION 2 -#define FALCOR_DEV_STAGE "" -#define FALCOR_DEV_REVISION 1 -#define FALCOR_VERSION_STRING "3.2.1" diff --git a/Framework/Source/Falcor.props b/Framework/Source/Falcor.props deleted file mode 100644 index b3996d5f5..000000000 --- a/Framework/Source/Falcor.props +++ /dev/null @@ -1,33 +0,0 @@ - - - - - $(SolutionDir)\.\Framework\FalcorSharedObjects\\..\ - FALCOR_D3D12 - - - $(SolutionDir)Bin\$(PlatformShortName)\$(Configuration)\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\$(ProjectName)\ - - - - Level3 - true - $(FALCOR_CORE_DIRECTORY);$(FALCOR_CORE_DIRECTORY)\Source\;$(FALCOR_CORE_DIRECTORY)\Externals\nvapi;$(FALCOR_CORE_DIRECTORY)\Externals\GLM;$(FALCOR_CORE_DIRECTORY)\Externals\VulkanSDK\Include;$(FALCOR_CORE_DIRECTORY)\Externals\RapidJson\include;$(FALCOR_CORE_DIRECTORY)\Externals\pybind11\include;$(FALCOR_CORE_DIRECTORY)\Externals\Python\include;$(FALCOR_CORE_DIRECTORY)\Externals - _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;GLM_FORCE_DEPTH_ZERO_TO_ONE;$(FALCOR_BACKEND);_UNICODE;UNICODE;%(PreprocessorDefinitions) - - - $(FALCOR_CORE_DIRECTORY)\Externals\FreeImage;$(FALCOR_CORE_DIRECTORY)\Externals\Assimp\lib\$(PlatformName)\;$(FALCOR_CORE_DIRECTORY)\Externals\FFMpeg\lib\$(PlatformName);$(FALCOR_CORE_DIRECTORY)\Externals\openvr\lib\win64;$(FALCOR_CORE_DIRECTORY)\Externals\nvapi\amd64;$(FALCOR_CORE_DIRECTORY)\Externals\VulkanSDK\Lib;$(FALCOR_CORE_DIRECTORY)\Externals\Slang\bin\windows-x64\release;$(FALCOR_CORE_DIRECTORY)\Externals\GLFW\lib;$(FALCOR_CORE_DIRECTORY)\Externals\Python\libs - glfw3dll.lib;slang.lib;Comctl32.lib;Shlwapi.lib;assimp.lib;freeimage.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;avcodec.lib;avutil.lib;avformat.lib;swscale.lib;Shcore.lib;%(AdditionalDependencies) - - - call $(FALCOR_CORE_DIRECTORY)\BuildScripts\postbuild.bat $(FALCOR_CORE_DIRECTORY)\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) $(FALCOR_BACKEND) - - - - - $(FALCOR_CORE_DIRECTORY) - true - - - \ No newline at end of file diff --git a/Framework/Source/Falcor.vcxproj b/Framework/Source/Falcor.vcxproj deleted file mode 100644 index 9fc0398cd..000000000 --- a/Framework/Source/Falcor.vcxproj +++ /dev/null @@ -1,1537 +0,0 @@ - - - - - DebugD3D12 - x64 - - - DebugVK - x64 - - - ReleaseD3D12 - x64 - - - ReleaseVK - x64 - - - - - - - - - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - false - false - true - true - - - true - false - false - true - - - true - true - - - true - false - false - true - - - false - false - true - true - - - true - false - false - true - - - true - true - false - false - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - true - - - true - false - false - true - - - false - false - true - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - true - true - false - false - - - true - false - false - true - - - - - - - - - - - - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - true - true - true - true - - - true - true - true - true - - - - - - - - - - - - - - - - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - true - true - - - true - false - false - true - - - true - false - false - true - - - true - false - false - true - - - - - - - - - - - - - - - - - - - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {2c535635-e4c5-4098-a928-574f0e7cd5f9} - - - - true - - - {3B602F0E-3834-4F73-B97D-7DFC91597A98} - Win32Proj - Falcor - 10.0.17763.0 - - - - StaticLibrary - true - v141 - Unicode - - - StaticLibrary - true - v141 - Unicode - - - StaticLibrary - false - v141 - false - Unicode - - - StaticLibrary - false - v141 - false - Unicode - - - - - - - - - - - - - - - - - - - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\Lib\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ - Clean - - - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\Lib\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ - Clean - - - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\Lib\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ - Clean - - - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\Lib\ - $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ - Clean - - - - - - Level3 - Disabled - _PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;IMGUI_API=__declspec(dllimport);FALCOR_D3D12;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE - $(ProjectDir);$(ProjectDir)\..\Externals\GLM;$(ProjectDir)\..\Externals\GLFW\include;$(ProjectDir)\..\Externals\FreeImage;$(ProjectDir)\..\Externals\ASSIMP\include;$(ProjectDir)\..\Externals\FFMpeg\include;$(ProjectDir)\..\Externals\OculusSDK\LibOVR\Include;$(ProjectDir)\..\Externals\OculusSDK\LibOVRKernel\Src;$(ProjectDir)\..\Externals\OpenVR\headers;$(ProjectDir)\..\Externals\RapidJson\include;$(ProjectDir)\..\Externals\VulkanSDK\Include;$(ProjectDir)\..\Externals\Python\Include;$(ProjectDir)\..\Externals\pybind11\include;$(ProjectDir)\..;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\nvapi;%(AdditionalIncludeDirectories) - true - true - false - - - Windows - true - - - - - - - - - - - - - - - - - - - - - - - Level3 - Disabled - _PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;IMGUI_API=__declspec(dllimport);FALCOR_VK;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE - $(ProjectDir);$(ProjectDir)\..\Externals\GLM;$(ProjectDir)\..\Externals\GLFW\include;$(ProjectDir)\..\Externals\FreeImage;$(ProjectDir)\..\Externals\ASSIMP\include;$(ProjectDir)\..\Externals\FFMpeg\include;$(ProjectDir)\..\Externals\OculusSDK\LibOVR\Include;$(ProjectDir)\..\Externals\OculusSDK\LibOVRKernel\Src;$(ProjectDir)\..\Externals\OpenVR\headers;$(ProjectDir)\..\Externals\RapidJson\include;$(ProjectDir)\..\Externals\VulkanSDK\Include;$(ProjectDir)\..\Externals\Python\Include;$(ProjectDir)\..\Externals\pybind11\include;$(ProjectDir)\..;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\nvapi;$(VK_SDK_PATH)\Include;%(AdditionalIncludeDirectories) - true - true - false - - - Windows - true - - - - - - - - - - - - - - - - - - - - - - - Level3 - - - MaxSpeed - true - true - _PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;IMGUI_API=__declspec(dllimport);FALCOR_D3D12;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE - $(ProjectDir);$(ProjectDir)\..\Externals\GLM;$(ProjectDir)\..\Externals\GLFW\include;$(ProjectDir)\..\Externals\FreeImage;$(ProjectDir)\..\Externals\ASSIMP\include;$(ProjectDir)\..\Externals\FFMpeg\include;$(ProjectDir)\..\Externals\OculusSDK\LibOVR\Include;$(ProjectDir)\..\Externals\OculusSDK\LibOVRKernel\Src;$(ProjectDir)\..\Externals\OpenVR\headers;$(ProjectDir)\..\Externals\RapidJson\include;$(ProjectDir)\..\Externals\VulkanSDK\Include;$(ProjectDir)\..\Externals\Python\Include;$(ProjectDir)\..\Externals\pybind11\include;$(ProjectDir)\..;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\nvapi;%(AdditionalIncludeDirectories) - true - true - - - Windows - true - true - true - - - - - - - - - - - - - - - %(AdditionalLibraryDirectories) - - - - - - - Level3 - - - MaxSpeed - true - true - _PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;IMGUI_API=__declspec(dllimport);FALCOR_VK;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE - $(ProjectDir);$(ProjectDir)\..\Externals\GLM;$(ProjectDir)\..\Externals\GLFW\include;$(ProjectDir)\..\Externals\FreeImage;$(ProjectDir)\..\Externals\ASSIMP\include;$(ProjectDir)\..\Externals\FFMpeg\include;$(ProjectDir)\..\Externals\OculusSDK\LibOVR\Include;$(ProjectDir)\..\Externals\OculusSDK\LibOVRKernel\Src;$(ProjectDir)\..\Externals\OpenVR\headers;$(ProjectDir)\..\Externals\RapidJson\include;$(ProjectDir)\..\Externals\VulkanSDK\Include;$(ProjectDir)\..\Externals\Python\Include;$(ProjectDir)\..\Externals\pybind11\include;$(ProjectDir)\..;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\nvapi;$(VK_SDK_PATH)\Include;%(AdditionalIncludeDirectories) - true - true - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Framework/Source/Falcor.vcxproj.filters b/Framework/Source/Falcor.vcxproj.filters deleted file mode 100644 index 761bba3e9..000000000 --- a/Framework/Source/Falcor.vcxproj.filters +++ /dev/null @@ -1,2836 +0,0 @@ - - - - - Graphics\Model - - - Graphics\Model - - - Graphics\Model - - - Graphics\Model - - - Graphics - - - Graphics - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Graphics\Model\Loaders - - - Graphics\Model\Loaders - - - Graphics\Model\Loaders - - - Graphics\Scene - - - Graphics\Scene - - - Graphics\Scene - - - Graphics\Model - - - Graphics\Camera - - - Graphics\Camera - - - Graphics\Scene - - - Graphics\Paths - - - Graphics\Paths - - - Graphics\Material - - - Utils\Video - - - Utils\Video - - - Utils\Video - - - Graphics - - - Graphics - - - Graphics\Model\Loaders - - - VR\OpenVR - - - VR\OpenVR - - - VR\OpenVR - - - VR\OpenVR - - - VR\OpenVR - - - Utils\Math - - - Utils\Psychophysics - - - Utils\Psychophysics - - - Utils - - - Effects\NormalMap - - - Effects\Shadows - - - Effects\Utils - - - Effects\SkyBox - - - Effects\ToneMapping - - - Graphics\Model\Loaders - - - VR - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API\LowLevel - - - API - - - - API - - - API - - - API - - - API - - - API - - - Graphics - - - Graphics - - - API - - - Graphics\Model\Loaders - - - Utils - - - Utils\Picking - - - Graphics\Scene\Editor - - - Graphics\Scene\Editor - - - Graphics\Scene\Editor - - - Utils - - - Effects\AmbientOcclusion - - - Utils - - - Effects\ParticleSystem - - - API - - - API\LowLevel - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan\LowLevel - - - API\Vulkan\LowLevel - - - API\Vulkan\LowLevel - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan - - - API\Vulkan\LowLevel - - - API\Vulkan\LowLevel - - - API\Vulkan - - - API\Vulkan\LowLevel - - - API - - - API\Vulkan - - - API - - - API - - - API - - - API - - - API - - - API - - - API\Vulkan - - - API\Vulkan - - - Effects\TAA - - - Utils - - - API - - - Graphics\Model - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Utils\Platform\Linux - - - Utils\Platform\Windows - - - Utils\Platform\Windows - - - Utils\Platform\Linux - - - Utils\Platform - - - Utils - - - Utils\Platform - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - Graphics - - - Graphics\Program - - - API\Vulkan - - - Effects\FXAA - - - Utils - - - Utils\PatternGenerators - - - Utils\PatternGenerators - - - Utils\Scripting - - - Externals\GLM\detail - - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing\RtProgram - - - Experimental\Raytracing\RtProgram - - - Experimental\Raytracing\RtProgram - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - - - - Graphics\Model - - - Graphics\Model - - - Graphics\Model - - - Graphics\Model - - - Graphics - - - Graphics - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Utils - - - Data - - - Utils - - - Utils - - - Utils - - - Graphics\Model\Loaders - - - Graphics\Model\Loaders - - - Graphics\Model\Loaders - - - Graphics\Model\Loaders - - - Graphics\Scene - - - Graphics\Scene - - - Graphics\Scene - - - Graphics\Model - - - Graphics\Camera - - - Graphics\Camera - - - Utils\Math - - - Utils\Math - - - Graphics\Scene - - - Graphics\Scene - - - Data - - - Graphics\Paths - - - Graphics\Paths - - - Graphics\Paths - - - Graphics\Material - - - Utils\Video - - - Utils\Video - - - Utils\Video - - - Graphics - - - Graphics - - - Graphics\Model\Loaders - - - VR\OpenVR - - - VR\OpenVR - - - VR\OpenVR - - - VR\OpenVR - - - VR\OpenVR - - - Utils\Math - - - Utils\Psychophysics - - - Utils\Psychophysics - - - Utils - - - Effects\NormalMap - - - Effects\Shadows - - - Effects\Utils - - - Effects\SkyBox - - - Effects\ToneMapping - - - Graphics\Model\Loaders - - - VR - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API - - - API\LowLevel - - - API\LowLevel - - - API\LowLevel - - - Utils - - - API - - - API\LowLevel - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavcodec - - - Externals\FFMpeg\libavdevice - - - Externals\FFMpeg\libavdevice - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavfilter - - - Externals\FFMpeg\libavformat - - - Externals\FFMpeg\libavformat - - - Externals\FFMpeg\libavformat - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libavutil - - - Externals\FFMpeg\libpostproc - - - Externals\FFMpeg\libpostproc - - - Externals\FFMpeg\libswresample - - - Externals\FFMpeg\libswresample - - - Externals\FFMpeg\libswscale - - - Externals\FFMpeg\libswscale - - - Externals\FreeImage - - - Externals\GLFW - - - Externals\GLFW - - - - - - - API - - - API - - - API - - - API - - - API - - - API - - - Graphics - - - Graphics - - - API - - - API - - - API - - - API\LowLevel - - - Graphics\Model - - - Graphics\Model\Loaders - - - Utils - - - Utils - - - Utils\Picking - - - Graphics\Scene\Editor - - - Graphics\Scene\Editor - - - Graphics\Scene\Editor - - - Utils - - - Effects\AmbientOcclusion - - - Utils - - - Data\Effects\Particles - - - API\Vulkan - - - Effects\ParticleSystem - - - API - - - API\LowLevel - - - API\Vulkan\LowLevel - - - API\Vulkan - - - Data\Effects\Particles - - - Data - - - Data - - - API\Vulkan - - - API - - - Effects\TAA - - - Utils - - - Utils - - - Utils\Platform - - - Utils\Platform - - - Utils - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - Graphics\Program - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12 - - - API\D3D12\LowLevel - - - API\D3D12\LowLevel - - - - Graphics\Model - - - Graphics - - - Graphics\Program - - - Data\Effects - - - Data\Effects - - - Effects\FXAA - - - Utils - - - Utils - - - Utils\PatternGenerators - - - Utils\PatternGenerators - - - Utils\PatternGenerators - - - Utils - - - Utils - - - Utils\Scripting - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM\simd - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - Externals\GLM - - - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing - - - Experimental\Raytracing\RtProgram - - - Experimental\Raytracing\RtProgram - - - Experimental\Raytracing\RtProgram - - - Experimental\Raytracing\RtProgram - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - Experimental\RenderPasses - - - - Experimental\RenderPasses - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - Experimental\RenderGraph - - - - - {91055aa0-2e25-4507-816e-5b43817cef35} - - - {be02830e-afa2-487f-9c2e-5d8096bbba61} - - - {e92b12af-efa7-464a-8e6e-70385c85a1b5} - - - {f2ec6f46-f660-4827-a0be-ecbf231b69b0} - - - {acefc4bf-9434-4454-8a62-201442c09368} - - - {f18d878f-edd9-46d4-99ea-c98642b95c7d} - - - {d1e7f435-1a6b-4b90-a3a7-7bae60b19ceb} - - - {10617e7e-6bba-4bb0-9081-56718efa0cb4} - - - {c3922fda-093e-4529-918a-118ffb465ae2} - - - {71f28ec9-9c98-499a-947e-a68f4e99f80c} - - - {c2c1f450-97ef-4896-9b26-efe09836bf15} - - - {917a7de6-1592-4ef8-9069-705faa5d74a2} - - - {b09c5e53-0f1c-4268-8e95-9502744022ef} - - - {8b58789c-729c-440d-82c3-30fb3ff7f3b3} - - - {cfa6e784-ac16-403c-8f1f-5072dfd47292} - - - {768c99a4-1e77-4850-94bb-4e62b28ed2b8} - - - {4deccfb4-2dac-4ee8-9ff2-6bca8562c8ab} - - - {5fafb5e1-2a48-4701-848e-366273e36f82} - - - {f47af4e6-20c2-4790-b35f-23a7992d2e60} - - - {aee22e98-b4c5-4edb-bc2e-eec38629fd89} - - - {64437661-2d27-4013-aaa6-60d43a36d3ab} - - - {36fcd9b5-801d-4bfe-bfa6-66b763e0a7e8} - - - {0a005ed9-f456-46d5-85c0-880689378c3b} - - - {aab19330-4eb2-405a-af5a-a4f65faa888a} - - - {6019fee5-465d-409d-92c2-499985e4d084} - - - {8e50a4a9-abda-4cc1-88d2-f048bb68b6e8} - - - {ab9efc68-3fb7-41e1-b23a-440841348a3e} - - - {88aa476e-4331-4ad8-b773-2161d97d8b50} - - - {68a68d99-0c61-45ed-b3e6-40bd7d24a13f} - - - {f0e71268-e340-4c20-a6dc-cd530de8c7fa} - - - {41a14cca-f426-4dd6-919c-76bbc359e065} - - - {751b1180-32da-49f6-af92-7a06f383530b} - - - {92632a4a-f346-4b12-8932-b9ef2bdf61bc} - - - {3f274d50-1f6e-4818-a2bf-3d139f127559} - - - {6e37b9dd-d391-420d-aa2e-d3a73598ab38} - - - {93980430-1a93-4999-bd8a-2f0b46713318} - - - {6165d5ad-97ac-4ff3-9c9e-68c93acc245f} - - - {8bb5d38b-eb7d-40a8-a5aa-c934669015b5} - - - {db06632d-289e-4e1c-85b4-7d6f5e41c73b} - - - {3b164303-abe4-428c-bd50-4368ab7b8933} - - - {872e05a2-6354-4a08-ada2-8b8c88a7858c} - - - {723ff80c-2107-49c1-8a2b-e01f9d4eb6e9} - - - {f191732c-c52a-4867-976e-f96cedcbf908} - - - {113ae44c-7970-429b-ba40-22c457f0003d} - - - {be06907b-0403-4c3b-a59a-ec74cbfdfb86} - - - {f191732c-c52a-4867-976e-f96cedcbf908} - - - {230e7726-da32-47d7-bdf6-c77e62c21ada} - - - {d1b8a19e-aea1-4b27-862a-06434b845be7} - - - {32802cf1-128c-4a2c-835a-17d0a3dd0976} - - - {91140a89-0755-46ae-8bea-7af2e775afb4} - - - {594e507a-0be5-4e74-bda2-ddf7b429e957} - - - {cfd2d470-4f60-4788-89e9-9aa4ee7032de} - - - {00ac78d3-41db-4095-af85-65088c6b60bd} - - - {eaf2bd94-5891-4207-b40a-3e2461da6295} - - - {a2a4ca1c-043f-4b99-8d25-5f413799c4c8} - - - {51b693f1-34da-4d5e-a6d5-925944c92c79} - - - {6fb02568-1f63-46b7-baee-85e961051a76} - - - {cc98beaa-c9ed-4457-98c6-89955706cd91} - - - {0193cc13-996d-498d-a32e-8990117f264d} - - - {17bed071-6e42-4df7-8389-70de86f01a8c} - - - {3c2596b8-a604-47c0-8627-5327a1b71a7b} - - - {90cb7750-bb00-4f68-bd33-c9fb300b18d0} - - - {a6e3acfa-a028-4555-a490-d51604b42b99} - - - {06fa6d05-49c9-43d3-8741-a6f1b441a43a} - - - {ed4fb26d-a218-4279-a55d-3f4053f09c4e} - - - {fd5e4329-273b-4802-ad4b-9720a882996e} - - - {e3b76813-257a-4b19-8517-03302aa9fe98} - - - {c211a7c2-a22a-4745-9a23-42e98dbff40c} - - - {60443751-4f7a-4361-adb2-d3e6edabecf8} - - - - - Data\Framework\Shaders - - - Data\Framework\Shaders - - - Data\Framework\Shaders - - - Data\Framework\Shaders - - - Data\Effects\Particles - - - Data\Effects\Particles - - - Data\Effects\Particles - - - Data\Effects\Particles - - - Data\Effects\Particles - - - Data\Effects\Particles - - - Data - - - ShadingUtils - - - Data - - - Data - - - Data\Framework\Shaders - - - Data\Framework\Shaders - - - ShadingUtils - - - ShadingUtils - - - Data\Framework\Shaders - - - ShadingUtils - - - Data\Framework\Shaders - - - ShadingUtils - - - Data\Framework\Shaders - - - Data\Framework\Shaders - - - Data\Framework\Shaders - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\Effects - - - Data\RenderPasses - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\detail - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\ext - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtc - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - Externals\GLM\gtx - - - \ No newline at end of file diff --git a/Framework/Source/Graphics/FboHelper.cpp b/Framework/Source/Graphics/FboHelper.cpp deleted file mode 100644 index fb02a1673..000000000 --- a/Framework/Source/Graphics/FboHelper.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "API/Formats.h" -#include "FboHelper.h" -#include "API/FBO.h" -#include "API/Texture.h" - -namespace Falcor -{ - namespace FboHelper - { - bool CheckParams(const std::string& Func, uint32_t width, uint32_t height, uint32_t arraySize, uint32_t mipLevels, uint32_t sampleCount) - { - std::string msg = "FboHelper::" + Func + "() - "; - std::string param; - - if(mipLevels == 0) - param = "mipLevels"; - else if(width == 0) - param = "width"; - else if(height == 0) - param = "height"; - else if(arraySize == 0) - param = "arraySize"; - else - { - if(sampleCount > 1 && mipLevels > 1) - { - logError(msg + "can't create multi-sampled texture with more than one mip-level. sampleCount = " + std::to_string(sampleCount) + ", mipLevels = " + std::to_string(mipLevels) + "."); - return false; - } - return true; - } - - logError(msg + param + " can't be zero."); - return false; - } - - static Texture::SharedPtr createTexture2D(uint32_t w, uint32_t h, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize, uint32_t mipLevels, Texture::BindFlags flags) - { - if (format == ResourceFormat::Unknown) - { - logError("Can't create Texture2D with an unknown resource format"); - return nullptr; - } - - Texture::SharedPtr pTex; - if (sampleCount > 1) - { - pTex = Texture::create2DMS(w, h, format, sampleCount, arraySize, flags); - } - else - { - pTex = Texture::create2D(w, h, format, arraySize, mipLevels, nullptr, flags); - } - - return pTex; - } - - static Texture::BindFlags getBindFlags(bool isDepth, bool allowUav) - { - Texture::BindFlags flags = Texture::BindFlags::ShaderResource; - flags |= isDepth ? Texture::BindFlags::DepthStencil : Texture::BindFlags::RenderTarget; - - if (allowUav) - { - flags |= Texture::BindFlags::UnorderedAccess; - } - return flags; - } - - Fbo::SharedPtr create2D(uint32_t width, uint32_t height, const Fbo::Desc& fboDesc, uint32_t arraySize, uint32_t mipLevels) - { - uint32_t sampleCount = fboDesc.getSampleCount(); - if(CheckParams("Create2D", width, height, arraySize, mipLevels, sampleCount) == false) - { - return nullptr; - } - - Fbo::SharedPtr pFbo = Fbo::create(); - - // create the color targets - for(uint32_t i = 0; i < Fbo::getMaxColorTargetCount(); i++) - { - if(fboDesc.getColorTargetFormat(i) != ResourceFormat::Unknown) - { - Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); - Texture::SharedPtr pTex = createTexture2D(width, height, fboDesc.getColorTargetFormat(i), sampleCount, arraySize, mipLevels, flags); - pFbo->attachColorTarget(pTex, i, 0, 0, Fbo::kAttachEntireMipLevel); - } - } - - if(fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) - { - Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); - Texture::SharedPtr pDepth = createTexture2D(width, height, fboDesc.getDepthStencilFormat(), sampleCount, arraySize, mipLevels, flags); - pFbo->attachDepthStencilTarget(pDepth, 0, 0, Fbo::kAttachEntireMipLevel); - } - - return pFbo; - } - - Fbo::SharedPtr createCubemap(uint32_t width, uint32_t height, const Fbo::Desc& fboDesc, uint32_t arraySize, uint32_t mipLevels) - { - if (fboDesc.getSampleCount() > 1) - { - logError("creatceCubemap() - can't create a multisampled FBO"); - return nullptr; - } - if(CheckParams("CreateCubemap", width, height, arraySize, mipLevels, 0) == false) - { - return nullptr; - } - - Fbo::SharedPtr pFbo = Fbo::create(); - - // create the color targets - for(uint32_t i = 0; i < Fbo::getMaxColorTargetCount(); i++) - { - Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); - auto pTex = Texture::createCube(width, height, fboDesc.getColorTargetFormat(i), arraySize, mipLevels, nullptr, flags); - pFbo->attachColorTarget(pTex, i, 0, Fbo::kAttachEntireMipLevel); - } - - if(fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) - { - Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); - auto pDepth = Texture::createCube(width, height, fboDesc.getDepthStencilFormat(), arraySize, mipLevels, nullptr, flags); - pFbo->attachDepthStencilTarget(pDepth, 0, Fbo::kAttachEntireMipLevel); - } - - return pFbo; - } - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/FboHelper.h b/Framework/Source/Graphics/FboHelper.h deleted file mode 100644 index 3335d46f6..000000000 --- a/Framework/Source/Graphics/FboHelper.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "glm/vec4.hpp" -#include "API/FBO.h" -#include "API/Texture.h" - -namespace Falcor -{ - /*! - * \addtogroup Falcor - * @{ - - Helper function to create an Fbo object and the required textures. - */ - - namespace FboHelper - { - /** Create a color-only 2D framebuffer. - \param[in] width Width of the render-targets. - \param[in] height Height of the render-targets. - \param[in] fboDesc Struct specifying the frame buffer's attachments and formats. - \param[in] arraySize Optional. The number of array slices in the texture. - \param[in] mipLevels Optional. The number of mip levels to create. You can use Texture#kMaxPossible to create the entire chain - */ - Fbo::SharedPtr create2D(uint32_t width, uint32_t height, const Fbo::Desc& fboDesc, uint32_t arraySize = 1, uint32_t mipLevels = 1); - - /** Create a color-only cubemap framebuffer. - \param[in] width width of the render-targets. - \param[in] height height of the render-targets. - \param[in] fboDesc Struct specifying the frame buffer's attachments and formats. - \param[in] arraySize Optional. The number of cubes in the texture. - \param[in] mipLevels Optional. The number of mip levels to create. You can use Texture#kMaxPossible to create the entire chain - */ - Fbo::SharedPtr createCubemap(uint32_t width, uint32_t height, const Fbo::Desc& fboDesc, uint32_t arraySize = 1, uint32_t mipLevels = 1); - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/FullScreenPass.cpp b/Framework/Source/Graphics/FullScreenPass.cpp deleted file mode 100644 index da6fda166..000000000 --- a/Framework/Source/Graphics/FullScreenPass.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "FullScreenPass.h" -#include "API/VAO.h" -#include "API/Buffer.h" -#include "API/DepthStencilState.h" -#include "API/RenderContext.h" -#include "API/VertexLayout.h" -#include "API/Window.h" - -namespace Falcor -{ - bool checkForViewportArray2Support() - { -#ifdef FALCOR_D3D12 - return false; -#elif defined FALCOR_VK - return false; -#else -#error Unknown API -#endif - } - struct Vertex - { - glm::vec2 screenPos; - glm::vec2 texCoord; - }; - -#ifdef FALCOR_VK -#define ADJUST_Y(a) (-(a)) -#else -#define ADJUST_Y(a) a -#endif - - static const Vertex kVertices[] = - { - {glm::vec2(-1, ADJUST_Y( 1)), glm::vec2(0, 0)}, - {glm::vec2(-1, ADJUST_Y(-1)), glm::vec2(0, 1)}, - {glm::vec2( 1, ADJUST_Y( 1)), glm::vec2(1, 0)}, - {glm::vec2( 1, ADJUST_Y(-1)), glm::vec2(1, 1)}, - }; -#undef ADJUST_Y - - static void initStaticObjects(Buffer::SharedPtr& pVB, Vao::SharedPtr& pVao) - { - // First time we got here. create VB and VAO - const uint32_t vbSize = (uint32_t)(sizeof(Vertex)*arraysize(kVertices)); - pVB = Buffer::create(vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, (void*)kVertices); - - // create VAO - VertexLayout::SharedPtr pLayout = VertexLayout::create(); - VertexBufferLayout::SharedPtr pBufLayout = VertexBufferLayout::create(); - pBufLayout->addElement("POSITION", 0, ResourceFormat::RG32Float, 1, 0); - pBufLayout->addElement("TEXCOORD", 8, ResourceFormat::RG32Float, 1, 1); - pLayout->addBufferLayout(0, pBufLayout); - - Vao::BufferVec buffers{ pVB }; - pVao = Vao::create(Vao::Topology::TriangleStrip, pLayout, buffers); - } - - FullScreenPass::UniquePtr FullScreenPass::create(const std::string& psFile, const Program::DefineList& programDefines, bool disableDepth, bool disableStencil, uint32_t viewportMask, bool enableSPS, Shader::CompilerFlags compilerFlags, const std::string& shaderModel) - { - return create("", psFile, programDefines, disableDepth, disableStencil, viewportMask, enableSPS, compilerFlags, shaderModel); - } - - FullScreenPass::UniquePtr FullScreenPass::create(const std::string& vsFile, const std::string& psFile, const Program::DefineList& programDefines, bool disableDepth, bool disableStencil, uint32_t viewportMask, bool enableSPS, Shader::CompilerFlags compilerFlags, const std::string& shaderModel) - { - UniquePtr pPass = UniquePtr(new FullScreenPass()); - pPass->init(vsFile, psFile, programDefines, disableDepth, disableStencil, viewportMask, enableSPS, compilerFlags, shaderModel); - return pPass; - } - - void FullScreenPass::init(const std::string& vsFile, const std::string& psFile, const Program::DefineList& programDefines, bool disableDepth, bool disableStencil, uint32_t viewportMask, bool enableSPS, Shader::CompilerFlags compilerFlags, const std::string& shaderModel) - { - mpPipelineState = GraphicsState::create(); - mpPipelineState->toggleSinglePassStereo(enableSPS); - - // create depth stencil state - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(!disableDepth); - dsDesc.setDepthWriteMask(!disableDepth); - dsDesc.setDepthFunc(DepthStencilState::Func::LessEqual); // Equal is needed to allow overdraw when z is enabled (e.g., background pass etc.) - dsDesc.setStencilTest(!disableStencil); - dsDesc.setStencilWriteMask(!disableStencil); - mpDepthStencilState = DepthStencilState::create(dsDesc); - - Program::DefineList defs = programDefines; - std::string gs; - - if(viewportMask) - { - defs.add("_VIEWPORT_MASK", std::to_string(viewportMask)); - if(checkForViewportArray2Support()) - { - defs.add("_USE_VP2_EXT"); - } - else - { - defs.add("_OUTPUT_VERTEX_COUNT", std::to_string(3 * popcount(viewportMask))); - gs = "Framework/Shaders/FullScreenPass.gs.slang"; - } - } - - GraphicsProgram::Desc d; - d.setCompilerFlags(compilerFlags); - d.addShaderLibrary(vsFile.empty() ? "Framework/Shaders/FullScreenPass.vs.slang" : vsFile).vsEntry("main").addShaderLibrary(psFile).psEntry("main"); - if (gs.size()) d.addShaderLibrary(gs).gsEntry("main"); - if (!shaderModel.empty()) d.setShaderModel(shaderModel); - mpProgram = GraphicsProgram::create(d, defs); - mpPipelineState->setProgram(mpProgram); - - if (gFullScreenData.pVertexBuffer == nullptr) - { - initStaticObjects(gFullScreenData.pVertexBuffer, gFullScreenData.pVao); - } - mpPipelineState->setVao(gFullScreenData.pVao); - } - - void FullScreenPass::execute(RenderContext* pRenderContext, DepthStencilState::SharedPtr pDsState, BlendState::SharedPtr pBlendState) const - { - mpPipelineState->pushFbo(pRenderContext->getGraphicsState()->getFbo(), false); - mpPipelineState->setViewport(0, pRenderContext->getGraphicsState()->getViewport(0), false); - mpPipelineState->setScissors(0, pRenderContext->getGraphicsState()->getScissors(0)); - - mpPipelineState->setVao(gFullScreenData.pVao); - mpPipelineState->setDepthStencilState(pDsState ? pDsState : mpDepthStencilState); - mpPipelineState->setBlendState(pBlendState); - pRenderContext->pushGraphicsState(mpPipelineState); - pRenderContext->draw(arraysize(kVertices), 0); - pRenderContext->popGraphicsState(); - mpPipelineState->popFbo(false); - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/FullScreenPass.h b/Framework/Source/Graphics/FullScreenPass.h deleted file mode 100644 index 47d07fc47..000000000 --- a/Framework/Source/Graphics/FullScreenPass.h +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "Graphics/Program/Program.h" -#include "API/VAO.h" -#include "API/DepthStencilState.h" -#include "API/Buffer.h" -#include "Graphics/Program/ProgramVersion.h" -#include "Graphics/GraphicsState.h" - -namespace Falcor -{ - class RenderContext; - - struct FullScreenPassData - { - Buffer::SharedPtr pVertexBuffer; - Vao::SharedPtr pVao; - uint64_t objectCount = 0; - }; - - dlldecl FullScreenPassData gFullScreenData; - - /** Helper class to simplify full-screen passes - */ - class FullScreenPass - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - ~FullScreenPass() - { - assert(gFullScreenData.objectCount > 0); - - gFullScreenData.objectCount--; - if (gFullScreenData.objectCount == 0) - { - gFullScreenData.pVao = nullptr; - gFullScreenData.pVertexBuffer = nullptr; - } - } - - - /** Create a new object. - \param[in] psFile Pixel shader filename. Can also be an absolute path or a relative path from a data directory. - \param[in] shaderDefines Optional. A list of macro definitions to be patched into the shaders. - \param[in] disableDepth Optional. Disable depth test (and therefore depth writes). This is the common case; however, e.g. writing depth in fullscreen passes can sometimes be useful. - \param[in] disableStencil Optional. As DisableDepth for stencil. - \param[in] viewportMask Optional. Value to initialize viewport mask with. Useful for multi-projection passes - \param[in] enableSPS Optional. If true, use Single-Pass Stereo when executing this pass. - \param[in] compilerFlags Optional. Shader compiler flags. - \param[in] shaderModel Optional. The shader model string. This depends on the API you are using. See Program::Desc::setShaderModel(). - */ - static UniquePtr create(const std::string& psFile, const Program::DefineList& programDefines = Program::DefineList(), bool disableDepth = true, bool disableStencil = true, uint32_t viewportMask = 0, bool enableSPS = false, Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None, const std::string& shaderModel = ""); - - /** Create a new object - \param[in] vsFile Vertex shader filename. Can also be an absolute path or a relative path from a data directory. - \param[in] psFile Pixel shader filename. Can also be an absolute path or a relative path from a data directory. - \param[in] shaderDefines Optional. A list of macro definitions to be patched into the shaders. - \param[in] disableDepth Optional. Disable depth test (and therefore depth writes). This is the common case; however, e.g. writing depth in fullscreen passes can sometimes be useful. - \param[in] disableStencil Optional. As DisableDepth for stencil. - \param[in] viewportMask Optional. Value to initialize viewport mask with. Useful for multi-projection passes - \param[in] enableSPS Optional. If true, use Single-Pass Stereo when executing this pass. - \param[in] compilerFlags Optional. Shader compiler flags. - \param[in] shaderModel Optional. The shader model string. This depends on the API you are using. See Program::Desc::setShaderModel(). - */ - static UniquePtr create(const std::string& vsFile, const std::string& psFile, const Program::DefineList& programDefines = Program::DefineList(), bool disableDepth = true, bool disableStencil = true, uint32_t viewportMask = 0, bool enableSPS = false, Shader::CompilerFlags compilerFlags = Shader::CompilerFlags::None, const std::string& shaderModel = ""); - - /** Execute the pass. - \param[in] pRenderContext The render context. - \param[in] pDsState Optional. Use it to make the pass use a different DS state then the one created during initialization - */ - void execute(RenderContext* pRenderContext, DepthStencilState::SharedPtr pDsState = nullptr, BlendState::SharedPtr pBlendState = nullptr) const; - - /** Get the program. - */ - const Program::SharedConstPtr getProgram() const { return mpProgram; } - Program::SharedPtr getProgram() { return mpProgram; } - - protected: - FullScreenPass() { gFullScreenData.objectCount++; } - void init(const std::string& vsFile, const std::string & psFile, const Program::DefineList& programDefines, bool disableDepth, bool disableStencil, uint32_t viewportMask, bool enableSPS, Shader::CompilerFlags compilerFlags, const std::string& shaderModel); - - private: - GraphicsProgram::SharedPtr mpProgram; - GraphicsState::SharedPtr mpPipelineState; - DepthStencilState::SharedPtr mpDepthStencilState; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Animation.cpp b/Framework/Source/Graphics/Model/Animation.cpp deleted file mode 100644 index 39a51f067..000000000 --- a/Framework/Source/Graphics/Model/Animation.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Animation.h" -#include "AnimationController.h" -#include "glm/gtc/matrix_transform.hpp" -#include "glm/gtx/transform.hpp" - -namespace Falcor -{ - Animation::UniquePtr Animation::create(const std::string& name, const std::vector& animationSets, float duration, float ticksPerSecond) - { - return UniquePtr(new Animation(name, animationSets, duration, ticksPerSecond)); - } - - Animation::UniquePtr Animation::create(const Animation& other) - { - return UniquePtr(new Animation(other)); - } - - Animation::Animation(const std::string& name, const std::vector& animationSets, float duration, float ticksPerSecond) : mName(name), mAnimationSets(animationSets), mDuration(duration), mTicksPerSecond(ticksPerSecond) - { - - } - - Animation::Animation(const Animation& other) : mName(other.mName), mDuration(other.mDuration), mTicksPerSecond(other.mTicksPerSecond) - { - mAnimationSets = other.mAnimationSets; - } - - Animation::~Animation() = default; - - template - uint32_t findCurrentFrame(T channel, float ticks) - { - uint32_t curKeyID = channel.lastKeyUsed; - while(curKeyID < channel.keys.size() - 1) - { - if(channel.keys[curKeyID + 1].time > ticks) - { - break; - } - curKeyID++; - } - return curKeyID; - } - - glm::vec3 interpolate(const glm::vec3& start, const glm::vec3& end, float ratio) - { - return start + ((end - start) * ratio); - } - - glm::quat interpolate(const glm::quat& start, const glm::quat& end, float ratio) - { - return glm::slerp(start, end, ratio); - } - - template - KeyType Animation::calcCurrentKey(AnimationChannel& channel, float ticks, float lastUpdateTime) - { - KeyType curValue; - if(channel.keys.size() > 0) - { - if(ticks < lastUpdateTime) - { - channel.lastKeyUsed = 0; - } - - // search for the next keyframe - uint32_t curKeyIndex = findCurrentFrame(channel, ticks); - uint32_t nextKeyIndex = (curKeyIndex + 1) % channel.keys.size(); - const AnimationKey& curKey = channel.keys[curKeyIndex]; - const AnimationKey& nextKey = channel.keys[nextKeyIndex]; - - assert(ticks >= curKey.time); - // Interpolate between them - float diff = nextKey.time - curKey.time; - - if (diff == 0) - { - curValue = curKey.value; - } - else - { - if (diff < 0) - { - diff += mDuration; - } - - float ratio = (ticks - curKey.time) / diff; - curValue = interpolate(curKey.value, nextKey.value, ratio); - } - - channel.lastKeyUsed = curKeyIndex; - } - return curValue; - } - - void Animation::animate(double totalTime, AnimationController* pAnimationController) - { - // Calculate the relative time - float ticks = (float)fmod(totalTime * mTicksPerSecond, mDuration); - - for(auto& Key : mAnimationSets) - { - glm::mat4 translation; - translation[3] = glm::vec4(calcCurrentKey(Key.translation, ticks, Key.lastUpdateTime), 1); - - glm::mat4 scaling; - if (Key.scaling.keys.size() > 0) - { - scaling = glm::scale(calcCurrentKey(Key.scaling, ticks, Key.lastUpdateTime)); - } - - glm::quat q = calcCurrentKey(Key.rotation, ticks, Key.lastUpdateTime); - glm::mat4 rotation = glm::mat4_cast(q); - - Key.lastUpdateTime = ticks; - - glm::mat4 T = translation * rotation * scaling; - pAnimationController->setBoneLocalTransform(Key.boneID, T); - } - } -} diff --git a/Framework/Source/Graphics/Model/Animation.h b/Framework/Source/Graphics/Model/Animation.h deleted file mode 100644 index ccd121d06..000000000 --- a/Framework/Source/Graphics/Model/Animation.h +++ /dev/null @@ -1,85 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "glm/vec3.hpp" -#include "glm/gtc/quaternion.hpp" - -namespace Falcor -{ - class AnimationController; - - class Animation - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - template - struct AnimationKey - { - T value; - float time; - }; - - template - struct AnimationChannel - { - std::vector> keys; - uint32_t lastKeyUsed = 0; - }; - - struct AnimationSet - { - uint32_t boneID; - AnimationChannel translation; - AnimationChannel scaling; - AnimationChannel rotation; - float lastUpdateTime = 0; - }; - - static UniquePtr create(const std::string& name, const std::vector& animationSets, float duration, float ticksPerSecond); - static UniquePtr create(const Animation& other); - ~Animation(); - void animate(double totalTime, AnimationController* pAnimationController); - const std::string& getName() const { return mName; } - - private: - Animation(const std::string& name, const std::vector& animationSets, float duration, float ticksPerSecond); - Animation(const Animation& other); - - const std::string mName; - float mDuration; - float mTicksPerSecond; - - std::vector mAnimationSets; - - template - _KeyType calcCurrentKey(AnimationChannel<_KeyType>& channel, float ticks, float lastUpdateTime); - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/AnimationController.cpp b/Framework/Source/Graphics/Model/AnimationController.cpp deleted file mode 100644 index 6ec31c45e..000000000 --- a/Framework/Source/Graphics/Model/AnimationController.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "AnimationController.h" -#include "Model.h" -#include -#include "Animation.h" -#include - -namespace Falcor -{ - void dumpBonesHeirarchy(const std::string& filename, Bone* pBone, uint32_t count) - { - std::ofstream dotfile; - dotfile.open(filename.c_str()); - - // Header - dotfile << "digraph BonesGraph {" << std::endl; - - for(uint32_t i = 0; i < count; i++) - { - const Bone& bone = pBone[i]; - if(bone.parentID != AnimationController::kInvalidBoneID) - { - std::string parent = pBone[bone.parentID].name; - std::string me = bone.name; - std::replace(parent.begin(), parent.end(), '.', '_'); - std::replace(me.begin(), me.end(), '.', '_'); - - dotfile << parent << " -> " << me << std::endl; - } - } - - // Close the file - dotfile << "}" << std::endl; // closing graph scope - dotfile.close(); - } - - AnimationController::UniquePtr AnimationController::create(const std::vector& Bones) - { - return UniquePtr(new AnimationController(Bones)); - } - - AnimationController::UniquePtr AnimationController::create(const AnimationController& other) - { - return UniquePtr(new AnimationController(other)); - } - - AnimationController::AnimationController(const std::vector& Bones) - { - mBones = Bones; - mBoneTransforms.resize(mBones.size()); - mBoneInvTransposeTransforms.resize(mBones.size()); - setActiveAnimation(kBindPoseAnimationId); - } - - AnimationController::AnimationController(const AnimationController& other) - { - mBones = other.mBones; - mBoneTransforms = other.mBoneTransforms; - mBoneInvTransposeTransforms = other.mBoneInvTransposeTransforms; - for (const auto& it : other.mAnimations) - { - mAnimations.push_back(Animation::create(*it)); - } - mActiveAnimation = other.mActiveAnimation; - } - - void AnimationController::addAnimation(Animation::UniquePtr pAnimation) - { - mAnimations.push_back(std::move(pAnimation)); - } - - AnimationController::~AnimationController() = default; - - void AnimationController::setBoneLocalTransform(uint32_t boneID, const glm::mat4& transform) - { - assert(boneID < mBones.size()); - mBones[boneID].localTransform = transform; - } - - void AnimationController::animate(double currentTime) - { - if(mActiveAnimation != kBindPoseAnimationId) - { - mAnimations[mActiveAnimation]->animate(currentTime, this); - } - - for(uint32_t i = 0; i < mBones.size(); i++) - { - mBones[i].globalTransform = mBones[i].localTransform; - if(mBones[i].parentID != kInvalidBoneID) - { - mBones[i].globalTransform = mBones[mBones[i].parentID].globalTransform * mBones[i].localTransform; - } - mBoneTransforms[i] = mBones[i].globalTransform * mBones[i].offset; - mBoneInvTransposeTransforms[i] = transpose(inverse(mBoneTransforms[i])); - } - } - - void AnimationController::setActiveAnimation(uint32_t id) - { - assert(id == kBindPoseAnimationId || id < mAnimations.size()); - mActiveAnimation = id; - if(id == kBindPoseAnimationId) - { - for(auto& bone : mBones) - { - bone.localTransform = bone.originalLocalTransform; - } - } - animate(0); - } - - const std::string& AnimationController::getAnimationName(uint32_t ID) const - { - return mAnimations[ID]->getName(); - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/AnimationController.h b/Framework/Source/Graphics/Model/AnimationController.h deleted file mode 100644 index e5c544862..000000000 --- a/Framework/Source/Graphics/Model/AnimationController.h +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "glm/mat4x4.hpp" -#include "Animation.h" - -namespace Falcor -{ - struct Bone - { - uint32_t parentID; - uint32_t boneID; - std::string name; - glm::mat4 offset; - glm::mat4 localTransform; - glm::mat4 originalLocalTransform; - glm::mat4 globalTransform; - }; - - class Model; - class AssimpModelImporter; - - class AnimationController - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - static const uint32_t kInvalidBoneID = -1; - static const uint32_t kBindPoseAnimationId = -1; - - static UniquePtr create(const std::vector& bones); - static UniquePtr create(const AnimationController& other); - ~AnimationController(); - - void addAnimation(Animation::UniquePtr pAnimation); - void animate(double currentTime); - - uint32_t getAnimationCount() const { return uint32_t(mAnimations.size()); } - const std::string& getAnimationName(uint32_t ID) const; - void setActiveAnimation(uint32_t id); - uint32_t getActiveAnimation() const {return mActiveAnimation;} - - const std::vector& getBoneMatrices() const { return mBoneTransforms; } - const std::vector& getBoneInvTransposeMatrices() const { return mBoneInvTransposeTransforms; } - uint32_t getBoneCount() const { return uint32_t(mBones.size()); } - - uint32_t getBoneIdFromName(const std::string& name) const; - void setBoneLocalTransform(uint32_t boneID, const glm::mat4& transform); - - private: - AnimationController(const std::vector& bones); - AnimationController(const AnimationController& other); - - std::vector mBones; - std::vector mBoneTransforms; - std::vector mBoneInvTransposeTransforms; - std::vector mAnimations; - - uint32_t mActiveAnimation = kBindPoseAnimationId; - - void calculateBoneTransforms(); - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.cpp b/Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.cpp deleted file mode 100644 index 650bfc15a..000000000 --- a/Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.cpp +++ /dev/null @@ -1,994 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "assimp/Importer.hpp" -#include "assimp/postprocess.h" -#include "assimp/scene.h" - -#include "Framework.h" -#include "AssimpModelImporter.h" -#include "Graphics/Model/Model.h" -#include "Graphics/Model/Animation.h" -#include "Graphics/Model/Mesh.h" -#include "Graphics/Model/AnimationController.h" -#include "API/Texture.h" -#include "API/Buffer.h" -#include "Utils/Platform/OS.h" -#include "Graphics/TextureHelper.h" -#include "API/VertexLayout.h" -#include "Data/VertexAttrib.h" -#include "Utils/StringUtils.h" -#include "API/Device.h" - -namespace Falcor -{ - static_assert(Mesh::kMaxBonesPerVertex == 4, "Fix the weights and IDs container below"); - using VertexWeightsVec = std::vector; - struct uvec8_4 - { - uint8_t u[4]; - uint8_t& operator[](uint32_t i) { return u[i]; } - }; - - using VertexIdsVec = std::vector; - - template - void generateSubmeshTangentData( - const std::vector& indices, - uint32_t vertexCount, - const posType* vertexPosData, - const glm::vec3* vertexNormalData, - const glm::vec2* texCrdData, - uint32_t texCrdCount, - glm::vec3* bitangentData); - - - void loadBones(const aiMesh* pAiMesh, VertexWeightsVec& weights, VertexIdsVec& ids, uint32_t vertexCount, const std::map& boneNameToIdMap) - { - if (pAiMesh->mNumBones > 0xff) - { - logError("Too many bones"); - } - - weights.resize(vertexCount); - ids.resize(vertexCount); - - for (uint32_t bone = 0; bone < pAiMesh->mNumBones; bone++) - { - const aiBone* pAiBone = pAiMesh->mBones[bone]; - uint32_t aiBoneID = boneNameToIdMap.at(std::string(pAiBone->mName.C_Str())); - - // The way Assimp works, the weights holds the IDs of the vertices it affects. - // We loop over all the weights, initializing the vertices data along the way - for (uint32_t weightID = 0; weightID < pAiBone->mNumWeights; weightID++) - { - // Get the vertex the current weight affects - const aiVertexWeight& aiWeight = pAiBone->mWeights[weightID]; - - // Get the address of the Bone ID and weight for the current vertex - uvec8_4& vertexIds = ids[aiWeight.mVertexId]; - vec4& vertexWeights = weights[aiWeight.mVertexId]; - - // Find the next unused slot in the bone array of the vertex, and initialize it with the current value - bool emptySlotFound = false; - for (uint32_t j = 0; j < Mesh::kMaxBonesPerVertex; j++) - { - if (vertexWeights[j] == 0) - { - vertexIds[j] = (uint8_t)aiBoneID; - vertexWeights[j] = aiWeight.mWeight; - emptySlotFound = true; - break; - } - } - - if (emptySlotFound == false) - { - logError("Too many bones"); - } - } - } - - // Now we need to normalize the weights for each vertex, since in some models the sum is larger than 1 - for (uint32_t i = 0; i < vertexCount; i++) - { - vec4& w = weights[i]; - - float f = 0; - // Sum the weights - for (int j = 0; j < Mesh::kMaxBonesPerVertex; j++) - { - f += w[j]; - } - w /= f; - } - } - - std::vector createIndexBufferData(const aiMesh* pAiMesh) - { - uint32_t indexCount = pAiMesh->mNumFaces * pAiMesh->mFaces[0].mNumIndices; - std::vector indices(indexCount); - const uint32_t firstFacePrimSize = pAiMesh->mFaces[0].mNumIndices; - - for (uint32_t i = 0; i < pAiMesh->mNumFaces; i++) - { - uint32_t primSize = pAiMesh->mFaces[i].mNumIndices; - assert(primSize == firstFacePrimSize); // Mesh contains mixed primitive types, can be solved using aiProcess_SortByPType - for (uint32_t j = 0; j < firstFacePrimSize; j++) - { - indices[i * firstFacePrimSize + j] = (uint32_t)(pAiMesh->mFaces[i].mIndices[j]); - } - } - - return indices; - } - - void genTangentSpace(aiMesh* pAiMesh) - { - if (pAiMesh->mFaces[0].mNumIndices == 3) - { - pAiMesh->mBitangents = new aiVector3D[pAiMesh->mNumVertices]; - - const glm::vec3* pPos = (glm::vec3*)pAiMesh->mVertices; - glm::vec3* pBi = (glm::vec3*)pAiMesh->mBitangents; - glm::vec3* pNormals = (glm::vec3*)pAiMesh->mNormals; - std::vector indices = createIndexBufferData(pAiMesh); - - uint32_t texCrdCount = 0; - std::vector texCrd; - if (pAiMesh->mTextureCoords[0] != nullptr) - { - texCrdCount = 1; - texCrd.resize(pAiMesh->mNumVertices); - for (size_t i = 0; i < pAiMesh->mNumVertices; ++i) - { - texCrd[i] = glm::vec2(pAiMesh->mTextureCoords[0][i].x, pAiMesh->mTextureCoords[0][i].y); - } - } - - generateSubmeshTangentData(indices, pAiMesh->mNumVertices, pPos, pNormals, (texCrdCount > 0 ? texCrd.data() : nullptr), texCrdCount, pBi); - } - } - - struct layoutsData - { - uint32_t pos; - std::string name; - ResourceFormat format; - }; - - static const layoutsData kLayoutData[VERTEX_LOCATION_COUNT] = - { - { VERTEX_POSITION_LOC, VERTEX_POSITION_NAME, ResourceFormat::RGB32Float }, - { VERTEX_NORMAL_LOC, VERTEX_NORMAL_NAME, ResourceFormat::RGB32Float }, - { VERTEX_BITANGENT_LOC, VERTEX_BITANGENT_NAME, ResourceFormat::RGB32Float }, - { VERTEX_TEXCOORD_LOC, VERTEX_TEXCOORD_NAME, ResourceFormat::RGB32Float }, //for some reason this is rgb - { VERTEX_LIGHTMAP_UV_LOC, VERTEX_LIGHTMAP_UV_NAME, ResourceFormat::RGB32Float }, //for some reason this is rgb - { VERTEX_BONE_WEIGHT_LOC, VERTEX_BONE_WEIGHT_NAME, ResourceFormat::RGBA32Float }, - { VERTEX_BONE_ID_LOC, VERTEX_BONE_ID_NAME, ResourceFormat::RGBA8Uint }, - { VERTEX_DIFFUSE_COLOR_LOC, VERTEX_DIFFUSE_COLOR_NAME, ResourceFormat::RGBA32Float }, - }; - - - glm::mat4 aiMatToGLM(const aiMatrix4x4& aiMat) - { - glm::mat4 glmMat; - glmMat[0][0] = aiMat.a1; glmMat[0][1] = aiMat.a2; glmMat[0][2] = aiMat.a3; glmMat[0][3] = aiMat.a4; - glmMat[1][0] = aiMat.b1; glmMat[1][1] = aiMat.b2; glmMat[1][2] = aiMat.b3; glmMat[1][3] = aiMat.b4; - glmMat[2][0] = aiMat.c1; glmMat[2][1] = aiMat.c2; glmMat[2][2] = aiMat.c3; glmMat[2][3] = aiMat.c4; - glmMat[3][0] = aiMat.d1; glmMat[3][1] = aiMat.d2; glmMat[3][2] = aiMat.d3; glmMat[3][3] = aiMat.d4; - - return glm::transpose(glmMat); - } - - static void setTexture(aiTextureType type, bool isObjFile, Material* pMaterial, Texture::SharedPtr pTexture) - { - switch (type) - { - case aiTextureType_DIFFUSE: - pMaterial->setBaseColorTexture(pTexture); - break; - case aiTextureType_SPECULAR: - pMaterial->setSpecularTexture(pTexture); - break; - case aiTextureType_EMISSIVE: - pMaterial->setEmissiveTexture(pTexture); - break; - case aiTextureType_HEIGHT: - case aiTextureType_DISPLACEMENT: - // OBJ doesn't support normal maps, so they are usually placed in the height map slot. For consistency with other formats, we move them to the normal map slot. - isObjFile ? pMaterial->setNormalMap(pTexture) : pMaterial->setHeightMap(pTexture); - break; - case aiTextureType_NORMALS: - pMaterial->setNormalMap(pTexture); - break; - case aiTextureType_AMBIENT: - pMaterial->setOcclusionMap(pTexture); - break; - case aiTextureType_LIGHTMAP: - pMaterial->setLightMap(pTexture); - break; - default: - logWarning("Unsupported Assimp texture type " + std::to_string(type)); - } - } - - bool isSrgbRequired(aiTextureType aiType, bool isSrgbRequested, uint32_t shadingModel) - { - if (isSrgbRequested == false) - { - return false; - } - - switch (aiType) - { - case aiTextureType_SPECULAR: - assert(shadingModel == ShadingModelMetalRough || shadingModel == ShadingModelSpecGloss); - return (shadingModel == ShadingModelSpecGloss); - case aiTextureType_DIFFUSE: - case aiTextureType_AMBIENT: - case aiTextureType_EMISSIVE: - case aiTextureType_LIGHTMAP: - case aiTextureType_REFLECTION: - return true; - case aiTextureType_HEIGHT: - case aiTextureType_NORMALS: - case aiTextureType_SHININESS: - case aiTextureType_OPACITY: - case aiTextureType_DISPLACEMENT: - return false; - default: - should_not_get_here(); - return false; - } - } - - void AssimpModelImporter::loadTextures(const aiMaterial* pAiMaterial, const std::string& folder, Material* pMaterial, bool isObjFile, bool useSrgb) - { - for (int i = 0; i < AI_TEXTURE_TYPE_MAX; ++i) - { - aiTextureType aiType = (aiTextureType)i; - - uint32_t textureCount = pAiMaterial->GetTextureCount(aiType); - - if (textureCount >= 1) - { - if (textureCount != 1) - { - logError("Can't create material with more then one texture per Type"); - return; - } - - // Get the texture name - aiString path; - pAiMaterial->GetTexture(aiType, 0, &path); - std::string s(path.data); - Texture::SharedPtr pTex = nullptr; - - if (s.empty()) - { - logWarning("Texture has empty file name, ignoring."); - continue; - } - - // Check if the texture was already loaded - const auto& a = mTextureCache.find(s); - if (a != mTextureCache.end()) - { - pTex = a->second; - } - else - { - // create a new texture - std::string fullpath = folder + '/' + s; - fullpath = replaceSubstring(fullpath, "\\", "/"); - pTex = createTextureFromFile(fullpath, true, isSrgbRequired(aiType, useSrgb, pMaterial->getShadingModel())); - if (pTex) - { - mTextureCache[s] = pTex; - } - } - - assert(pTex != nullptr); - setTexture(aiType, isObjFile, pMaterial, pTex); - } - } - - // Flush upload heap after every material so we don't accumulate a ton of memory usage when loading a model with a lot of textures - gpDevice->flushAndSync(); - } - - Material::SharedPtr AssimpModelImporter::createMaterial(const aiMaterial* pAiMaterial, const std::string& folder, bool isObjFile, bool useSrgb) - { - aiString name; - pAiMaterial->Get(AI_MATKEY_NAME, name); - - // Parse the name - std::string nameStr = std::string(name.C_Str()); - std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower); - auto nameVec = splitString(nameStr, "."); // The name might contain information about the material - Material::SharedPtr pMaterial = Material::create(nameVec[0]); - - // Determine shading model. - // MetalRough is the default for everything except OBJ. Check that both flags aren't set simultaneously. - assert(!(is_set(mFlags, Model::LoadFlags::UseSpecGlossMaterials) && is_set(mFlags, Model::LoadFlags::UseMetalRoughMaterials))); - if (is_set(mFlags, Model::LoadFlags::UseSpecGlossMaterials) || (isObjFile && !is_set(mFlags, Model::LoadFlags::UseMetalRoughMaterials))) - { - pMaterial->setShadingModel(ShadingModelSpecGloss); - } - - // Load textures. Note that loading is affected by the current shading model. - loadTextures(pAiMaterial, folder, pMaterial.get(), isObjFile, useSrgb); - - // Opacity - float opacity; - if (pAiMaterial->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) - { - vec4 diffuse = pMaterial->getBaseColor(); - diffuse.a = opacity; - pMaterial->setBaseColor(diffuse); - } - - // Bump scaling - float bumpScaling; - if (pAiMaterial->Get(AI_MATKEY_BUMPSCALING, bumpScaling) == AI_SUCCESS) - { - float bumpOffset = pMaterial->getHeightOffset(); - pMaterial->setHeightScaleOffset(bumpScaling, bumpOffset); - } - - // Shininess - float shininess; - if (pAiMaterial->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) - { - // Convert OBJ/MTL Phong exponent to glossiness. - if (isObjFile) - { - float roughness = convertShininessToRoughness(shininess); - shininess = 1.f - roughness; - } - vec4 spec = pMaterial->getSpecularParams(); - spec.a = shininess; - pMaterial->setSpecularParams(spec); - } - - // Refraction - float refraction; - if (pAiMaterial->Get(AI_MATKEY_REFRACTI, refraction) == AI_SUCCESS) pMaterial->setIndexOfRefraction(refraction); - - // Diffuse color - aiColor3D color; - if (pAiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) - { - vec4 diffuse = vec4(color.r, color.g, color.b, pMaterial->getBaseColor().a); - pMaterial->setBaseColor(diffuse); - } - - // Specular color - if (pAiMaterial->Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) - { - vec4 specular = vec4(color.r, color.g, color.b, pMaterial->getSpecularParams().a); - pMaterial->setSpecularParams(specular); - } - - // Emissive color - if (pAiMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) - { - vec3 emissive = vec3(color.r, color.g, color.b); - pMaterial->setEmissiveColor(emissive); - } - // Double-Sided - int isDoubleSided; - if (pAiMaterial->Get(AI_MATKEY_TWOSIDED, isDoubleSided) == AI_SUCCESS) - { - pMaterial->setDoubleSided((isDoubleSided != 0)); - } - - // Parse the information contained in the name - // The first part is the material name, the other parts provides some info about the material properties - if (nameVec.size() > 1) - { - for (size_t i = 1; i < nameVec.size(); i++) - { - if (nameVec[i] == "doublesided") pMaterial->setDoubleSided(true); - else logWarning("Unknown material property found in the material's name - `" + nameVec[i] + "`"); - } - } - return pMaterial; - } - - bool verifyScene(const aiScene* pScene) - { - bool b = true; - - // No internal textures - if (pScene->mTextures != 0) - { - logError("Model has internal textures"); - b = false; - } - return b; - } - - AssimpModelImporter::AssimpModelImporter(Model& model, Model::LoadFlags flags) : mFlags(flags), mModel(model) - { - } - - bool AssimpModelImporter::createAllMaterials(const aiScene* pScene, const std::string& modelFolder, bool isObjFile, bool useSrgb) - { - for (uint32_t i = 0; i < pScene->mNumMaterials; i++) - { - const aiMaterial* pAiMaterial = pScene->mMaterials[i]; - auto pMaterial = createMaterial(pAiMaterial, modelFolder, isObjFile, useSrgb); - if (pMaterial == nullptr) - { - logError("Can't allocate memory for material"); - return false; - } - auto pAdded = checkForExistingMaterial(pMaterial); - if (pMaterial != pAdded) - { - // Material already exists - pMaterial = pAdded; - } - mAiMaterialToFalcor[i] = pMaterial; - } - - return true; - } - - bool AssimpModelImporter::parseAiSceneNode(const aiNode* pCurrent, const aiScene* pScene, IdToMesh& aiToFalcorMesh) - { - if (pCurrent->mNumMeshes) - { - // Init the transformation - aiMatrix4x4 transform = pCurrent->mTransformation; - const aiNode* pParent = pCurrent->mParent; - while (pParent) - { - transform *= pParent->mTransformation; - pParent = pParent->mParent; - } - - // Initialize the meshes - for (uint32_t i = 0; i < pCurrent->mNumMeshes; i++) - { - uint32_t aiId = pCurrent->mMeshes[i]; - - // New mesh - if (aiToFalcorMesh.find(aiId) == aiToFalcorMesh.end()) - { - // Cache mesh - aiToFalcorMesh[aiId] = createMesh(pScene->mMeshes[aiId]); - } - - mModel.addMeshInstance(aiToFalcorMesh[aiId], aiMatToGLM(transform)); - } - } - - bool b = true; - // visit the children - for (uint32_t i = 0; i < pCurrent->mNumChildren; i++) - { - b |= parseAiSceneNode(pCurrent->mChildren[i], pScene, aiToFalcorMesh); - } - return b; - } - - bool AssimpModelImporter::createDrawList(const aiScene* pScene) - { - createAnimationController(pScene); - IdToMesh aiToFalcorMeshId; - aiNode* pRoot = pScene->mRootNode; - return parseAiSceneNode(pRoot, pScene, aiToFalcorMeshId); - } - - bool AssimpModelImporter::initModel(const std::string& filename) - { - std::string fullpath; - if (findFileInDataDirectories(filename, fullpath) == false) - { - logError(std::string("Can't find model file ") + filename, true); - return false; - } - - uint32_t assimpFlags = aiProcessPreset_TargetRealtime_MaxQuality | - aiProcess_OptimizeGraph | - aiProcess_FlipUVs | - 0; - - if(is_set(mFlags, Model::LoadFlags::FindDegeneratePrimitives) == false) assimpFlags &= ~aiProcess_FindDegenerates; - if(is_set(mFlags, Model::LoadFlags::DontMergeMeshes)) assimpFlags &= ~aiProcess_OptimizeMeshes; // Avoid merging original meshes - if(is_set(mFlags, Model::LoadFlags::RemoveInstancing)) assimpFlags |= aiProcess_PreTransformVertices; - - // Never use Assimp's tangent gen code - assimpFlags &= ~(aiProcess_CalcTangentSpace); - - Assimp::Importer importer; - const aiScene* pScene = importer.ReadFile(fullpath, assimpFlags); - - if((pScene == nullptr) || (verifyScene(pScene) == false)) - { - std::string str("Can't open model file '"); - str = str + std::string(filename) + "'\n" + importer.GetErrorString(); - logError(str, true); - return false; - } - - // Extract the folder name - auto last = fullpath.find_last_of("/\\"); - std::string modelFolder = fullpath.substr(0, last); - - // Order of initialization matters, materials, bones and animations need to loaded before mesh initialization - bool isObjFile = hasSuffix(filename, ".obj", false); - bool useSrgbTextures = !is_set(mFlags, Model::LoadFlags::AssumeLinearSpaceTextures); - if(createAllMaterials(pScene, modelFolder, isObjFile, useSrgbTextures) == false) - { - logError(std::string("Can't create materials for model ") + filename, true); - return false; - } - - if (createDrawList(pScene) == false) - { - logError(std::string("Can't create draw lists for model ") + filename, true); - return false; - } - - return true; - } - - bool AssimpModelImporter::import(Model& model, const std::string& filename, Model::LoadFlags flags) - { - AssimpModelImporter loader(model, flags); - return loader.initModel(filename); - } - - bool AssimpModelImporter::isUsedNode(const aiNode* pNode) const - { - return (mBoneNameToIdMap.count(pNode->mName.C_Str()) > 0) || (mAdditionalUsedNodes.count(pNode) > 0); - } - - uint32_t AssimpModelImporter::initBone(const aiNode* pCurNode, uint32_t parentID, uint32_t boneID) - { - assert(isUsedNode(pCurNode)); - assert(pCurNode->mNumMeshes == 0); - - auto it = mBoneNameToIdMap.find(pCurNode->mName.C_Str()); - if (it != mBoneNameToIdMap.end()) - { - it->second = boneID; - } - - assert(boneID < mBones.size()); - Bone& bone = mBones[boneID]; - bone.name = pCurNode->mName.C_Str(); - bone.parentID = parentID; - bone.boneID = boneID; - bone.localTransform = aiMatToGLM(pCurNode->mTransformation); - bone.originalLocalTransform = bone.localTransform; - bone.globalTransform = bone.localTransform; - - if (parentID != AnimationController::kInvalidBoneID) - { - bone.globalTransform *= mBones[parentID].globalTransform; - } - - boneID++; - - for (uint32_t i = 0; i < pCurNode->mNumChildren; i++) - { - // Check that the child is actually used - const aiNode* pChild = pCurNode->mChildren[i]; - if (isUsedNode(pChild)) - { - boneID = initBone(pChild, bone.boneID, boneID); - } - } - return boneID; - } - - void AssimpModelImporter::initializeBonesOffsetMatrices(const aiScene* pScene) - { - for (uint32_t meshID = 0; meshID < pScene->mNumMeshes; meshID++) - { - const aiMesh* pAiMesh = pScene->mMeshes[meshID]; - - for (uint32_t boneID = 0; boneID < pAiMesh->mNumBones; boneID++) - { - const aiBone* pAiBone = pAiMesh->mBones[boneID]; - auto boneIt = mBoneNameToIdMap.find(pAiBone->mName.C_Str()); - assert(boneIt != mBoneNameToIdMap.end()); - Bone& bone = mBones[boneIt->second]; - bone.offset = aiMatToGLM(pAiBone->mOffsetMatrix); - } - } - } - - void AssimpModelImporter::initializeBones(const aiScene* pScene) - { - // Go over all the meshes, and find the bones that are being used - for (uint32_t i = 0; i < pScene->mNumMeshes; i++) - { - const aiMesh* pMesh = pScene->mMeshes[i]; - if (pMesh->HasBones()) - { - for (uint32_t j = 0; j < pMesh->mNumBones; j++) - { - mBoneNameToIdMap[pMesh->mBones[j]->mName.C_Str()] = AnimationController::kInvalidBoneID; - } - } - } - - if (mBoneNameToIdMap.size() != 0) - { - // For every bone used, all its ancestors are bones too. Mark them - for (auto it = mBoneNameToIdMap.begin(); it != mBoneNameToIdMap.end(); it++) - { - aiNode* pCurNode = pScene->mRootNode->FindNode(it->first.c_str()); - while (pCurNode) - { - // Used bones are already recorded, only record additional nodes - if (mBoneNameToIdMap.count(pCurNode->mName.C_Str()) == 0) - { - mAdditionalUsedNodes.insert(pCurNode); - } - pCurNode = pCurNode->mParent; - } - } - - // Now create the hierarchy - size_t hierarchySize = mBoneNameToIdMap.size() + mAdditionalUsedNodes.size(); - mBones.resize(hierarchySize); - uint32_t nodeBoneCount = initBone(pScene->mRootNode, AnimationController::kInvalidBoneID, 0); - assert(uint32_t(hierarchySize) == nodeBoneCount); - - initializeBonesOffsetMatrices(pScene); - } - } - - void AssimpModelImporter::createAnimationController(const aiScene* pScene) - { - initializeBones(pScene); - - // Create animation controller as long as there are bones. - // This will render bind pose if there are no animations. - if (mBones.empty() == false) - { - auto pAnimCtrl = AnimationController::create(mBones); - - for (uint32_t i = 0; i < pScene->mNumAnimations; i++) - { - Animation::UniquePtr pAnimation = createAnimation(pScene->mAnimations[i]); - pAnimCtrl->addAnimation(std::move(pAnimation)); - } - - mModel.setAnimationController(std::move(pAnimCtrl)); - } - } - - - Animation::UniquePtr AssimpModelImporter::createAnimation(const aiAnimation* pAiAnim) - { - assert(pAiAnim->mNumMeshChannels == 0); - float duration = float(pAiAnim->mDuration); - float ticksPerSecond = pAiAnim->mTicksPerSecond ? float(pAiAnim->mTicksPerSecond) : 25; - - std::vector animationSets; - animationSets.resize(pAiAnim->mNumChannels); - for (auto& animSet : animationSets) - { - animSet.boneID = AnimationController::kInvalidBoneID; - } - - for (uint32_t i = 0; i < pAiAnim->mNumChannels; i++) - { - const aiNodeAnim* pAiNode = pAiAnim->mChannels[i]; - - // If the bone is not used, skip it - const auto& idIt = mBoneNameToIdMap.find(pAiNode->mNodeName.C_Str()); - if (idIt == mBoneNameToIdMap.end()) continue; - - animationSets[i].boneID = idIt->second; - - for (uint32_t j = 0; j < pAiNode->mNumPositionKeys; j++) - { - const aiVectorKey& key = pAiNode->mPositionKeys[j]; - Animation::AnimationKey position; - position.value = glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z); - position.time = float(key.mTime); - animationSets[i].translation.keys.push_back(position); - } - - for (uint32_t j = 0; j < pAiNode->mNumScalingKeys; j++) - { - const aiVectorKey& key = pAiNode->mScalingKeys[j]; - Animation::AnimationKey scale; - scale.value = glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z); - scale.time = float(key.mTime); - animationSets[i].scaling.keys.push_back(scale); - } - - for (uint32_t j = 0; j < pAiNode->mNumRotationKeys; j++) - { - const aiQuatKey& key = pAiNode->mRotationKeys[j]; - Animation::AnimationKey rotation; - rotation.value = glm::quat(key.mValue.w, key.mValue.x, key.mValue.y, key.mValue.z); - rotation.time = float(key.mTime); - animationSets[i].rotation.keys.push_back(rotation); - } - } - - // Erase empty animation sets - animationSets.erase( - std::remove_if( - animationSets.begin(), - animationSets.end(), - [](const Animation::AnimationSet& a) { return a.boneID == AnimationController::kInvalidBoneID; }), - animationSets.end() - ); - - return Animation::create(std::string(pAiAnim->mName.C_Str()), animationSets, duration, ticksPerSecond); - } - - BoundingBox createMeshBbox(const aiMesh* pAiMesh) - { - vec3 boxMin(FLT_MAX); - vec3 boxMax(-FLT_MAX); - for (uint32_t vertexID = 0; vertexID < pAiMesh->mNumVertices; vertexID++) - { - glm::vec3 xyz(pAiMesh->mVertices[vertexID].x, pAiMesh->mVertices[vertexID].y, pAiMesh->mVertices[vertexID].z); - boxMin = glm::min(boxMin, xyz); - boxMax = glm::max(boxMax, xyz); - } - - return BoundingBox::fromMinMax(boxMin, boxMax); - } - - Mesh::SharedPtr AssimpModelImporter::createMesh(aiMesh* pAiMesh) - { - uint32_t vertexCount = pAiMesh->mNumVertices; - uint32_t indexCount = pAiMesh->mNumFaces * pAiMesh->mFaces[0].mNumIndices; - auto pIB = createIndexBuffer(pAiMesh); - BoundingBox boundingBox = createMeshBbox(pAiMesh); - - const bool generateTangentSpace = (pAiMesh->HasTangentsAndBitangents() == false) && (is_set(mFlags, Model::LoadFlags::DontGenerateTangentSpace) == false); - if (generateTangentSpace) - { - genTangentSpace(pAiMesh); - } - - VertexLayout::SharedPtr pLayout = createVertexLayout(pAiMesh); - if (pLayout == nullptr) - { - assert(0); - return nullptr; - } - - std::vector pVBs(pLayout->getBufferCount()); - - // Initialize the bones data - VertexWeightsVec weights; - VertexIdsVec ids; - if (pAiMesh->HasBones()) - { - loadBones(pAiMesh, weights, ids, vertexCount, mBoneNameToIdMap); - } - - // Create corresponding vertex buffers - for (uint32_t i = 0; i < pLayout->getBufferCount(); i++) - { - const VertexBufferLayout* pVbLayout = pLayout->getBufferLayout(i).get(); - pVBs[i] = createVertexBuffer(pAiMesh, pVbLayout, (uint8_t*)ids.data(), weights.data()); - } - - Vao::Topology topology = Vao::Topology::TriangleList; - switch (pAiMesh->mFaces[0].mNumIndices) - { - case 1: - topology = Vao::Topology::PointList; - break; - case 2: - topology = Vao::Topology::LineList; - break; - case 3: - topology = Vao::Topology::TriangleList; - break; - default: - logError(std::string("Error when creating mesh. Unknown topology with " + std::to_string(pAiMesh->mFaces[0].mNumIndices) + " indices.")); - assert(0); - } - - auto pMaterial = mAiMaterialToFalcor[pAiMesh->mMaterialIndex]; - assert(pMaterial); - - Mesh::SharedPtr pMesh = Mesh::create(pVBs, vertexCount, pIB, indexCount, pLayout, topology, pMaterial, boundingBox, pAiMesh->HasBones()); - - if (generateTangentSpace) - { - safe_delete_array(pAiMesh->mBitangents); - } - - return pMesh; - } - - Buffer::SharedPtr AssimpModelImporter::createIndexBuffer(const aiMesh* pAiMesh) - { - std::vector indices = createIndexBufferData(pAiMesh); - const uint32_t size = (uint32_t)(sizeof(uint32_t) * indices.size()); - Buffer::BindFlags bindFlags = Buffer::BindFlags::Index; - if (is_set(mFlags, Model::LoadFlags::BuffersAsShaderResource)) - { - bindFlags |= Buffer::BindFlags::ShaderResource; - } - return Buffer::create(size, bindFlags, Buffer::CpuAccess::None, indices.data());; - } - - - bool isElementUsed(const aiMesh* pAiMesh, uint32_t location) - { - switch (location) - { - case VERTEX_POSITION_LOC: - return true; - case VERTEX_NORMAL_LOC: - return pAiMesh->HasNormals(); - case VERTEX_BITANGENT_LOC: - return (pAiMesh->mBitangents != nullptr); // ASSIMP doesn't have a function that checks only for bitangents - case VERTEX_BONE_WEIGHT_LOC: - case VERTEX_BONE_ID_LOC: - return pAiMesh->HasBones(); - case VERTEX_DIFFUSE_COLOR_LOC: - return pAiMesh->HasVertexColors(0); - case VERTEX_TEXCOORD_LOC: - return pAiMesh->HasTextureCoords(0); - case VERTEX_LIGHTMAP_UV_LOC: - return pAiMesh->HasTextureCoords(1); - case VERTEX_PREV_POSITION_LOC: - return false; - default: - should_not_get_here(); - return false; - } - } - - VertexLayout::SharedPtr AssimpModelImporter::createVertexLayout(const aiMesh* pAiMesh) - { - static const uint32_t kMaxSupportedUVs = 2; - // Must have position!!! - if (pAiMesh->HasPositions() == false) - { - logError("AssimpModelImporter: Loaded mesh with no positions!"); - return nullptr; - } - - if (pAiMesh->GetNumUVChannels() > kMaxSupportedUVs) - { - logWarning("AssimpModelImporter: Loaded mesh with more then " + std::to_string(kMaxSupportedUVs) + " UV channels. Ignoring extra UVs"); - } - - for (uint32_t i = 0; i < min(pAiMesh->GetNumUVChannels(), kMaxSupportedUVs); i++) - { - if (pAiMesh->HasTextureCoords(i) == false) - { - logError("AssimpModelImporter: Unsupported texture coordinate set used in model."); - return nullptr; - } - } - - VertexLayout::SharedPtr pLayout = VertexLayout::create(); - - uint32_t bufferCount = 0; - for (uint32_t location = 0; location < VERTEX_LOCATION_COUNT; ++location) - { - if (isElementUsed(pAiMesh, location)) - { - VertexBufferLayout::SharedPtr pVbLayout = VertexBufferLayout::create(); - pVbLayout->addElement(kLayoutData[location].name, 0, kLayoutData[location].format, 1, location); - pLayout->addBufferLayout(bufferCount, pVbLayout); - bufferCount++; - } - } - - return pLayout; - } - - Buffer::SharedPtr AssimpModelImporter::createVertexBuffer(const aiMesh* pAiMesh, const VertexBufferLayout* pLayout, const uint8_t* pBoneIds, const vec4* pBoneWeights) - { - const uint32_t vertexStride = pLayout->getStride(); - std::vector initData(vertexStride * pAiMesh->mNumVertices, 0); - - for (uint32_t vertexID = 0; vertexID < pAiMesh->mNumVertices; vertexID++) - { - uint8_t* pVertex = &initData[vertexStride * vertexID]; - - for (uint32_t elementID = 0; elementID < pLayout->getElementCount(); elementID++) - { - uint32_t offset = pLayout->getElementOffset(elementID); - uint32_t location = pLayout->getElementShaderLocation(elementID); - uint8_t* pDst = pVertex + offset; - - uint8_t* pSrc = nullptr; - uint32_t size = 0; - switch (location) - { - case VERTEX_POSITION_LOC: - pSrc = (uint8_t*)(&pAiMesh->mVertices[vertexID]); - size = sizeof(pAiMesh->mVertices[0]); - break; - case VERTEX_NORMAL_LOC: - pSrc = (uint8_t*)(&pAiMesh->mNormals[vertexID]); - size = sizeof(pAiMesh->mNormals[0]); - break; - case VERTEX_BITANGENT_LOC: - pSrc = (uint8_t*)(&pAiMesh->mBitangents[vertexID]); - size = sizeof(pAiMesh->mBitangents[0]); - break; - case VERTEX_DIFFUSE_COLOR_LOC: - pSrc = (uint8_t*)(&pAiMesh->mColors[0][vertexID]); - size = sizeof(pAiMesh->mColors[0][0]); - break; - case VERTEX_TEXCOORD_LOC: - if (pAiMesh->mTextureCoords[0][vertexID].z != 0.f) - { - Falcor::logErrorAndExit("AssimpModelImporter::createVertexBuffer: Texcoord[0].z != 0.0"); - } - pSrc = (uint8_t*)(&pAiMesh->mTextureCoords[0][vertexID]); - size = sizeof(pAiMesh->mTextureCoords[0][vertexID]); - break; - case VERTEX_LIGHTMAP_UV_LOC: - if (pAiMesh->mTextureCoords[1][vertexID].z != 0.f) - { - Falcor::logErrorAndExit("AssimpModelImporter::createVertexBuffer: Texcoord[1].z != 0.0"); - } - pSrc = (uint8_t*)(&pAiMesh->mTextureCoords[1][vertexID]); - size = sizeof(pAiMesh->mTextureCoords[1][vertexID]); - break; - case VERTEX_BONE_WEIGHT_LOC: - pSrc = (uint8_t*)(&pBoneWeights[vertexID]); - size = sizeof(pBoneWeights[vertexID]); - break; - case VERTEX_BONE_ID_LOC: - pSrc = (uint8_t*)(&pBoneIds[vertexID * 4]); - size = sizeof(uint8_t) * 4; - break; - default: - should_not_get_here(); - continue; - } - - memcpy(pDst, pSrc, size); - } - } - Buffer::BindFlags bindFlags = Buffer::BindFlags::Vertex; - if (is_set(mFlags, Model::LoadFlags::BuffersAsShaderResource)) - { - bindFlags |= Buffer::BindFlags::ShaderResource; - } - - return Buffer::create(vertexStride * pAiMesh->mNumVertices, bindFlags, Buffer::CpuAccess::None, initData.data());; - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.h b/Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.h deleted file mode 100644 index 47bdb84d0..000000000 --- a/Framework/Source/Graphics/Model/Loaders/AssimpModelImporter.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include -#include "Graphics/Model/Loaders/ModelImporter.h" -#include "../AnimationController.h" -#include "../Mesh.h" -#include "../Model.h" - -struct aiScene; -struct aiNode; -struct aiAnimation; -struct aiMesh; -struct aiMaterial; -namespace Assimp -{ - class Importer; -} - -namespace Falcor -{ - class Animation; - class Buffer; - class VertexBufferLayout; - class Texture; - - /** Implements model import functionality through ASSIMP. - Typically, the user should use Model::createFromFile() to load a model instead of this class. - */ - class AssimpModelImporter : public ModelImporter - { - public: - /** Load a model using ASSIMP - \param[out] model Model object to load into - \param[in] filename Model's filename. Can include a full path or a relative path from a data directory - \param[in] flags Flags controlling model creation - \return Whether import succeeded - */ - static bool import(Model& model, const std::string& filename, Model::LoadFlags flags); - - private: - - using IdToMesh = std::unordered_map; - - AssimpModelImporter(Model& model, Model::LoadFlags flags); - AssimpModelImporter(const AssimpModelImporter&) = delete; - void operator=(const AssimpModelImporter&) = delete; - - bool initModel(const std::string& filename); - bool createDrawList(const aiScene* pScene); - bool parseAiSceneNode(const aiNode* pCurrent, const aiScene* pScene, IdToMesh& aiToFalcorMesh); - bool createAllMaterials(const aiScene* pScene, const std::string& modelFolder, bool isObjFile, bool useSrgb); - - void createAnimationController(const aiScene* pScene); - void initializeBones(const aiScene* pScene); - uint32_t initBone(const aiNode* pNode, uint32_t parentID, uint32_t boneID); - void initializeBonesOffsetMatrices(const aiScene* pScene); - - Animation::UniquePtr createAnimation(const aiAnimation* pAiAnim); - - Mesh::SharedPtr createMesh(aiMesh* pAiMesh); - VertexLayout::SharedPtr createVertexLayout(const aiMesh* pAiMesh); - Buffer::SharedPtr createIndexBuffer(const aiMesh* pAiMesh); - Buffer::SharedPtr createVertexBuffer(const aiMesh* pAiMesh, const VertexBufferLayout* pLayout, const uint8_t* pBoneIds, const vec4* pBoneWeights); - void loadTextures(const aiMaterial* pAiMaterial, const std::string& folder, Material* pMaterial, bool isObjFile, bool useSrgb); - Material::SharedPtr createMaterial(const aiMaterial* pAiMaterial, const std::string& folder, bool isObjFile, bool useSrgb); - - // Checks whether a node or its name corresponds to a used bone or node in the skeleton hierarchy - bool isUsedNode(const aiNode* pNode) const; - - std::map mBoneNameToIdMap; - // Non-bone nodes need to be counted by pointer because they can have duplicate names after assimp processing (e.g. "RootNode") - std::unordered_set mAdditionalUsedNodes; - - std::map mAiMaterialToFalcor; - - Model& mModel; - - std::vector mBones; - Model::LoadFlags mFlags; - std::map mTextureCache; - }; -} diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryImage.cpp b/Framework/Source/Graphics/Model/Loaders/BinaryImage.cpp deleted file mode 100644 index d633985e5..000000000 --- a/Framework/Source/Graphics/Model/Loaders/BinaryImage.cpp +++ /dev/null @@ -1,1141 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "BinaryImage.hpp" -//#include "gpu/CudaModule.hpp" -#include - -using namespace FW; - -//------------------------------------------------------------------------ - -#define C8(TYPE, OFS) { ChannelType_ ## TYPE, ChannelFormat_Clamp, OFS, 1, 0, 8 } -#define C16(TYPE, OFS, SIZE) { ChannelType_ ## TYPE, ChannelFormat_Clamp, 0, 2, OFS, SIZE } -#define C32(TYPE, OFS) { ChannelType_ ## TYPE, ChannelFormat_Clamp, 0, 4, OFS, 8 } -#define CF32(TYPE, OFS) { ChannelType_ ## TYPE, ChannelFormat_Float, OFS, 4, 0, 32 } - -const ImageFormat::StaticFormat ImageFormat::s_staticFormats[] = -{ - /* R8_G8_B8 */ { 3, 3, { C8(R,0), C8(G,1), C8(B,2) }, /*GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, false */}, - /* R8_G8_B8_A8 */ { 4, 4, { C8(R,0), C8(G,1), C8(B,2), C8(A,3) }, /*GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false */}, - /* A8 */ { 1, 1, { C8(A,0) }, /*GL_ALPHA8, GL_ALPHA, GL_UNSIGNED_BYTE, false */}, - /* XBGR_8888 */ { 4, 3, { C32(R,0), C32(G,8), C32(B,16) }, /*GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, true */}, - /* ABGR_8888 */ { 4, 4, { C32(R,0), C32(G,8), C32(B,16), C32(A,24) }, /*GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, true */}, - - /* RGB_565 */ { 2, 3, { C16(R,11,5), C16(G,5,6), C16(B,0,5) }, /*GL_RGB5, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false */}, - /* RGBA_5551 */ { 2, 4, { C16(R,11,5), C16(G,6,5), C16(B,1,5), C16(A,0,1) }, /*GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, false */}, - - /* RGB_Vec3f */ { 12, 3, { CF32(R,0), CF32(G,4), CF32(B,8) }, /*GL_RGB32F, GL_RGB, GL_FLOAT, false */}, - /* RGBA_Vec4f */ { 16, 4, { CF32(R,0), CF32(G,4), CF32(B,8), CF32(A,12) }, /*GL_RGBA32F, GL_RGBA, GL_FLOAT, false */}, - /* A_F32 */ { 4, 1, { CF32(A,0) }, /*GL_ALPHA32F_ARB, GL_ALPHA, GL_FLOAT, false */}, - - /*BGRA_8888*/ { 4, 4, { C8(R,0), C8(G,1), C8(B,2), C8(A, 3) }, /*GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, false */}, - /*BGR_888*/ { 3, 3, { C8(R,0), C8(G,1), C8(B,2) }, /*GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE, false */}, - /*RG_88*/ { 2, 2, { C8(R,0), C8(G,1), }, /*GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false */}, - /*R8*/ { 1, 1, { C8(R,0)}, /*GL_R, GL_RED, GL_UNSIGNED_BYTE, false */}, - - /*S3TC_DXT1*/ { 0, 0, {}, /*GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_NONE, false*/}, - /*S3TC_DXT3*/ { 0, 0, {}, /*GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_NONE, false*/}, - /*S3TC_DXT5*/ { 0, 0, {}, /*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_NONE, false*/}, - /*RGTC_R*/ { 0, 0, {}, /*GL_COMPRESSED_RED_RGTC1, GL_RED, GL_NONE, false*/}, - /*RGTC_RG*/ { 0, 0, {}, /*GL_COMPRESSED_RG_RGTC2, GL_RG, GL_NONE, false*/}, -}; - -#undef C8 -#undef C16 -#undef C32 -#undef CF32 - -//------------------------------------------------------------------------ - -#define RGB_565_TO_ABGR_8888(V) ((V >> 8) & 0x000000F8) | (V >> 13) | ((V << 5) & 0x0000FC00) | ((V >> 1) & 0x00000300) | ((V << 19) & 0x00F80000) | ((V >> 14) & 0x00070000) | 0xFF000000 -#define ABGR_8888_TO_RGB_565(V) (U16)(((V << 8) & 0xF800) | ((V >> 5) & 0x07E0) | ((V >> 19) & 0x001F)) - -#define RGBA_5551_TO_ABGR_8888(V) ((V >> 8) & 0x000000F8) | (V >> 13) | ((V << 5) & 0x0000F800) | (V & 0x00000700) | ((V << 18) & 0x00F80000) | ((V >> 13) & 0x00070000) | ((S32)(V << 31) >> 7) -#define ABGR_8888_TO_RGBA_5551(V) (U16)(((V << 8) & 0xF800) | ((V >> 5) & 0x07C0) | ((V >> 18) & 0x003E) | (V >> 31)) - -//------------------------------------------------------------------------ - -ImageFormat::ID ImageFormat::getID(void) const -{ - if (m_id != ID_Max) - return m_id; - - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - for (int i = 0; i < (int)ID_Generic; i++) - { - const StaticFormat& f = s_staticFormats[i]; - if (m_genericBPP == f.bpp && - m_genericChannels.size() == f.numChannels && - std::memcmp(m_genericChannels.data(), f.channels, m_genericChannels.size()*sizeof(m_genericChannels[0])) == 0) - { - m_id = (ID)i; - return m_id; - } - } - - m_id = ID_Generic; - return m_id; -} - -//------------------------------------------------------------------------ - -const ImageFormat::StaticFormat* ImageFormat::getStaticFormat(void) const -{ - ID id = getID(); - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (id < ID_Generic) ? &s_staticFormats[id] : NULL; -} - -//------------------------------------------------------------------------ - -int ImageFormat::getBPP(void) const -{ - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (m_id < ID_Generic) ? s_staticFormats[m_id].bpp : m_genericBPP; -} - -//------------------------------------------------------------------------ - -int ImageFormat::getNumChannels(void) const -{ - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (m_id < ID_Generic) ? s_staticFormats[m_id].numChannels : (int)m_genericChannels.size(); -} - -//------------------------------------------------------------------------ - -const ImageFormat::Channel& ImageFormat::getChannel(int idx) const -{ - FW_ASSERT(idx >= 0 && idx < getNumChannels()); - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (m_id < ID_Generic) ? s_staticFormats[m_id].channels[idx] : m_genericChannels[idx]; -} - -//------------------------------------------------------------------------ - -int ImageFormat::findChannel(ChannelType Type) const -{ - int num = getNumChannels(); - for (int i = 0; i < num; i++) - if (getChannel(i).Type == Type) - return i; - return -1; -} - -//------------------------------------------------------------------------ - -void ImageFormat::set(const ImageFormat& other) -{ - m_id = other.m_id; - if (m_id >= ID_Generic) - { - m_genericBPP = other.m_genericBPP; - m_genericChannels = other.m_genericChannels; - } -} - -//------------------------------------------------------------------------ - -void ImageFormat::clear(void) -{ - m_id = ID_Generic; - m_genericBPP = 0; - m_genericChannels.clear(); -} - -//------------------------------------------------------------------------ - -void ImageFormat::addChannel(const Channel& channel) -{ - if (m_id < ID_Generic) - { - const StaticFormat& f = s_staticFormats[m_id]; - m_genericBPP = f.bpp; - m_genericChannels.resize(f.numChannels); - for(int32_t i = 0 ; i < f.numChannels ; i++) - { - m_genericChannels[i] = f.channels[i]; - } - } - - m_id = ID_Max; - m_genericBPP = max(m_genericBPP, channel.wordOfs + channel.wordSize); - m_genericChannels.push_back(channel); -} - -//------------------------------------------------------------------------ - -#if 0 -ImageFormat::ID ImageFormat::getGLFormat(void) const -{ - const ImageFormat::StaticFormat* sf = getStaticFormat(); - - // Requires little endian machine => check. - - if (sf && sf->glLittleEndian) - { - U32 tmp = 0x12345678; - if (*(U8*)&tmp != 0x78) - sf = NULL; - } - - // Maps directly to a GL format => done. - - if (sf && sf->glInternalFormat != GL_NONE) - return getID(); - - // Otherwise => select the closest match. - - U32 channels = 0; - bool isFloat = false; - for (int i = 0; i < getNumChannels(); i++) - { - const ImageFormat::Channel& c = getChannel(i); - if (c.Type > ImageFormat::ChannelType_A) - continue; - - channels |= 1 < c.Type; - if (c.format == ImageFormat::ChannelFormat_Float) - isFloat = true; - } - - if ((channels & 7) == 0) - return (isFloat) ? ImageFormat::A_F32 : ImageFormat::A8; - if ((channels & 8) == 0) - return (isFloat) ? ImageFormat::RGB_Vec3f : ImageFormat::R8_G8_B8; - return (isFloat) ? ImageFormat::RGBA_Vec4f : ImageFormat::R8_G8_B8_A8; -} - -//------------------------------------------------------------------------ - -bool ImageFormat::operator==(const ImageFormat& other) const -{ - if (m_id < ID_Generic || other.m_id < ID_Generic) - return (getID() == other.getID()); - - return ( - m_genericBPP == other.m_genericBPP && - m_genericChannels.size() == other.m_genericChannels.size() && - memcmp(m_genericChannels.data(), other.m_genericChannels.data(), m_genericChannels.size() * sizeof(m_genericChannels[0]) == 0)); -} - -//------------------------------------------------------------------------ -Image::Image(const Vec2i& size, const ImageFormat& format, void* ptr, S64 stride) -{ - init(size, format); - FW_ASSERT(size.min() == 0 || ptr); - - S64 lo = 0; - S64 hi = 0; - if (size.min() != 0) - { - lo = min(stride * (size.y - 1), (S64)0); - hi = max(stride * (size.y - 1), (S64)0) + size.x * format.getBPP(); - } - - m_stride = stride; - m_buffer = new Buffer((U8*)ptr + lo, hi - lo); - m_ownBuffer = true; - m_offset = -lo; -} - -//------------------------------------------------------------------------ - -Image::Image(const Vec2i& size, const ImageFormat& format, Buffer& buffer, S64 ofs, S64 stride) -{ - init(size, format); - FW_ASSERT(size.min() == 0 || ofs + min(stride * (size.y - 1), (S64)0) >= 0); - FW_ASSERT(size.min() == 0 || ofs + max(stride * (size.y - 1), (S64)0) + size.x * format.getBPP() <= buffer.getSize()); - - m_stride = stride; - m_buffer = &buffer; - m_ownBuffer = false; - m_offset = ofs; -} - -//------------------------------------------------------------------------ - -Image::~Image(void) -{ - if (m_ownBuffer) - delete m_buffer; -} - -//------------------------------------------------------------------------ - -U32 Image::getABGR(const Vec2i& pos) const -{ - FW_ASSERT(contains(pos, 1)); - const U8* p = getPtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::R8_G8_B8: return p[0] | (p[1] << 8) | (p[2] << 16) | 0xFF000000; - case ImageFormat::R8_G8_B8_A8: return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); - case ImageFormat::A8: return *p << 24; - case ImageFormat::XBGR_8888: return *(const U32*)p | 0xFF000000; - case ImageFormat::ABGR_8888: return *(const U32*)p; - - case ImageFormat::RGB_565: { U16 v = *(const U16*)p; return RGB_565_TO_ABGR_8888(v); } - case ImageFormat::RGBA_5551: { U16 v = *(const U16*)p; return RGBA_5551_TO_ABGR_8888(v); } - - case ImageFormat::RGB_Vec3f: return Vec4f(*(const Vec3f*)p, 1.0f).toABGR(); - case ImageFormat::RGBA_Vec4f: return ((const Vec4f*)p)->toABGR(); - case ImageFormat::A_F32: return clamp((int)(*(const F32*)p * 255.0f + 0.5f), 0x00, 0xFF) << 24; - - default: - { - getChannels(pos); - bool hasAlpha = false; - U32 value = 0; - - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - U32 v = clamp((int)(m_channelTmp[i] * 255.0f + 0.5f), 0x00, 0xFF); - switch (m_format.getChannel(i).Type) - { - case ImageFormat::ChannelType_R: value |= v; break; - case ImageFormat::ChannelType_G: value |= v << 8; break; - case ImageFormat::ChannelType_B: value |= v << 16; break; - case ImageFormat::ChannelType_A: value |= v << 24; hasAlpha = true; break; - } - } - - if (!hasAlpha) - value |= 0xFF000000; - return value; - } - } -} - -//------------------------------------------------------------------------ - -void Image::setABGR(const Vec2i& pos, U32 value) -{ - FW_ASSERT(contains(pos, 1)); - U8* p = getMutablePtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::R8_G8_B8: p[0] = (U8)value; p[1] = (U8)(value >> 8); p[2] = (U8)(value >> 16); break; - case ImageFormat::R8_G8_B8_A8: p[0] = (U8)value; p[1] = (U8)(value >> 8); p[2] = (U8)(value >> 16); p[3] = (U8)(value >> 24); break; - case ImageFormat::A8: *p = (U8)(value >> 24); break; - case ImageFormat::XBGR_8888: *(U32*)p = value; break; - case ImageFormat::ABGR_8888: *(U32*)p = value; break; - - case ImageFormat::RGB_565: *(U16*)p = ABGR_8888_TO_RGB_565(value); break; - case ImageFormat::RGBA_5551: *(U16*)p = ABGR_8888_TO_RGBA_5551(value); break; - - case ImageFormat::RGB_Vec3f: *(Vec3f*)p = Vec4f::fromABGR(value).getXYZ(); break; - case ImageFormat::RGBA_Vec4f: *(Vec4f*)p = Vec4f::fromABGR(value); break; - case ImageFormat::A_F32: *(F32*)p = (F32)(value >> 24) / 255.0f; break; - - default: - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - F32& channel = m_channelTmp[i]; - switch (m_format.getChannel(i).Type) - { - case ImageFormat::ChannelType_R: channel = (F32)(value & 0xFF) / 255.0f; break; - case ImageFormat::ChannelType_G: channel = (F32)((value >> 8) & 0xFF) / 255.0f; break; - case ImageFormat::ChannelType_B: channel = (F32)((value >> 16) & 0xFF) / 255.0f; break; - case ImageFormat::ChannelType_A: channel = (F32)(value >> 24) / 255.0f; break; - default: channel = 0.0f; break; - } - } - setChannels(pos, m_channelTmp.getPtr()); - break; - } -} - -//------------------------------------------------------------------------ - -Vec4f Image::getVec4f(const Vec2i& pos) const -{ - FW_ASSERT(contains(pos, 1)); - const U8* p = getPtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::A8: return Vec4f(0.0f, 0.0f, 0.0f, (F32)(*p / 255.0f)); - case ImageFormat::XBGR_8888: return Vec4f::fromABGR(*(const U32*)p | 0xFF000000); - case ImageFormat::ABGR_8888: return Vec4f::fromABGR(*(const U32*)p); - case ImageFormat::RGB_Vec3f: return Vec4f(*(const Vec3f*)p, 1.0f); - case ImageFormat::RGBA_Vec4f: return *(const Vec4f*)p; - case ImageFormat::A_F32: return Vec4f(0.0f, 0.0f, 0.0f, *(const F32*)p); - - case ImageFormat::R8_G8_B8: - case ImageFormat::R8_G8_B8_A8: - case ImageFormat::RGB_565: - case ImageFormat::RGBA_5551: - return Vec4f::fromABGR(getABGR(pos)); - - default: - { - getChannels(pos); - Vec4f value(0.0f, 0.0f, 0.0f, 1.0f); - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - ImageFormat::ChannelType t = m_format.getChannel(i).Type; - if (t <= ImageFormat::ChannelType_A) - value[t] = m_channelTmp[i]; - } - return value; - } - } -} - -//------------------------------------------------------------------------ - -void Image::setVec4f(const Vec2i& pos, const Vec4f& value) -{ - FW_ASSERT(contains(pos, 1)); - U8* p = getMutablePtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::A8: *p = (U8)clamp((int)(value.w * 255.0f + 0.5f), 0x00, 0xFF); break; - case ImageFormat::XBGR_8888: *(U32*)p = value.toABGR(); break; - case ImageFormat::ABGR_8888: *(U32*)p = value.toABGR(); break; - case ImageFormat::RGB_Vec3f: *(Vec3f*)p = value.getXYZ(); break; - case ImageFormat::RGBA_Vec4f: *(Vec4f*)p = value; break; - case ImageFormat::A_F32: *(F32*)p = value.w; break; - - case ImageFormat::R8_G8_B8: - case ImageFormat::R8_G8_B8_A8: - case ImageFormat::RGB_565: - case ImageFormat::RGBA_5551: - setABGR(pos, value.toABGR()); - break; - - default: - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - ImageFormat::ChannelType t = m_format.getChannel(i).Type; - m_channelTmp[i] = (t <= ImageFormat::ChannelType_A) ? value[t] : 0.0f; - } - setChannels(pos, m_channelTmp.getPtr()); - break; - } -} - -//------------------------------------------------------------------------ - -void Image::flipX(void) -{ - int bpp = getBPP(); - for (int y = 0; y < m_size.y; y++) - { - U8* ptrA = getMutablePtr(Vec2i(0, y)); - U8* ptrB = getMutablePtr(Vec2i(m_size.x - 1, y)); - for (int x = (m_size.x >> 1); x > 0; x--) - { - for (int i = 0; i < bpp; i++) - swap(ptrA[i], ptrB[i]); - ptrA += bpp; - ptrB -= bpp; - } - } -} - -//------------------------------------------------------------------------ - -void Image::flipY(void) -{ - int scanBytes = m_size.x * getBPP(); - Array tmp(NULL, scanBytes); - for (int y = (m_size.y >> 1) - 1; y >= 0; y--) - { - U8* ptrA = getMutablePtr(Vec2i(0, y)); - U8* ptrB = getMutablePtr(Vec2i(0, m_size.y - 1 - y)); - memcpy(tmp.getPtr(), ptrA, scanBytes); - memcpy(ptrA, ptrB, scanBytes); - memcpy(ptrB, tmp.getPtr(), scanBytes); - } -} - -//------------------------------------------------------------------------ - -GLuint Image::createGLTexture(ImageFormat::ID desiredFormat, bool generateMipmaps) const -{ - // Select format. - - ImageFormat::ID formatID; - if (desiredFormat == ImageFormat::ID_Max) - formatID = m_format.getGLFormat(); - else - formatID = ImageFormat(desiredFormat).getGLFormat(); - - const ImageFormat::StaticFormat* sf = ImageFormat(formatID).getStaticFormat(); - FW_ASSERT(sf); - - // Image data not usable directly => convert. - - Image* converted = NULL; - const Image* img = this; - if (m_size.min() == 0 || m_format.getID() != formatID || m_stride != getBPP() * m_size.x) - { - converted = new Image(max(m_size, 1), formatID); - converted->set(*this); - img = converted; - } - - // create texture. - - GLContext::staticInit(); - - GLint oldTex = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); - - GLuint tex = 0; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, generateMipmaps); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (generateMipmaps) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // Uncomment to enable anisotropic filtering: -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, FW_S32_MAX); - - glTexImage2D(GL_TEXTURE_2D, 0, sf->glInternalFormat, - img->getSize().x, img->getSize().y, - 0, sf->glFormat, sf->glType, img->getPtr()); - - glBindTexture(GL_TEXTURE_2D, oldTex); - GLContext::checkErrors(); - - // Clean up. - - delete converted; - return tex; -} - -//------------------------------------------------------------------------ - -ImageFormat Image::chooseCudaFormat(CUDA_ARRAY_DESCRIPTOR* desc, ImageFormat::ID desiredFormat) const -{ -#if (!FW_USE_CUDA) - - FW_UNREF(desc); - FW_UNREF(desiredFormat); - fail("Image::chooseCudaFormat(): Built without FW_USE_CUDA!"); - return m_format; - -#else - - // Gather requirements. - - ImageFormat refFormat = m_format; - if (desiredFormat != ImageFormat::ID_Max) - refFormat = desiredFormat; - - int numChannels = min(refFormat.getNumChannels(), 4); - int channelBits = 0; - bool isFloat = false; - for (int i = 0; i < numChannels; i++) - { - const ImageFormat::Channel& chan = refFormat.getChannel(i); - channelBits = max(channelBits, chan.fieldSize); - isFloat = (chan.format == ImageFormat::ChannelFormat_Float); - } - - // Select format. - - CUarray_format datatype; - int wordSize; - - if (isFloat) datatype = CU_AD_FORMAT_FLOAT, wordSize = 4; - else if (channelBits <= 8) datatype = CU_AD_FORMAT_UNSIGNED_INT8, wordSize = 1; - else if (channelBits <= 16) datatype = CU_AD_FORMAT_UNSIGNED_INT16, wordSize = 2; - else datatype = CU_AD_FORMAT_UNSIGNED_INT32, wordSize = 4; - - ImageFormat formatA; // word per channel - ImageFormat formatB; // single word - - for (int i = 0; i < numChannels; i++) - { - const ImageFormat::Channel& ref = refFormat.getChannel(i); - - ImageFormat::Channel chan; - chan.Type = ref.Type; - chan.format = (isFloat) ? ImageFormat::ChannelFormat_Float : ref.format; - - chan.wordOfs = i * wordSize; - chan.wordSize = wordSize; - chan.fieldOfs = 0; - chan.fieldSize = wordSize * 8; - formatA.addChannel(chan); - - chan.wordOfs = 0; - chan.wordSize = wordSize * numChannels; - chan.fieldOfs = i * wordSize * 8; - chan.fieldSize = wordSize * 8; - formatB.addChannel(chan); - } - - // Fill in the descriptor. - - if (desc) - { - memset(desc, 0, sizeof(CUDA_ARRAY_DESCRIPTOR)); - desc->Width = m_size.x; - desc->Height = m_size.y; - desc->Format = datatype; - desc->NumChannels = numChannels; - } - return (formatB == refFormat) ? formatB : formatA; - -#endif -} - -//------------------------------------------------------------------------ - -CUarray Image::createCudaArray(ImageFormat::ID desiredFormat, ImageFormat* formatOut, CUDA_ARRAY_DESCRIPTOR* arrayDescOut) const -{ -#if (!FW_USE_CUDA) - - FW_UNREF(desiredFormat); - FW_UNREF(formatOut); - FW_UNREF(arrayDescOut); - fail("Image::createCudaArray(): Built without FW_USE_CUDA!"); - return NULL; - -#else - - // Choose format. - - CUDA_ARRAY_DESCRIPTOR arrayDesc; - ImageFormat cudaFormat = chooseCudaFormat(&arrayDesc, desiredFormat); - - // Image data not usable directly => convert. - - Image* converted = NULL; - const Image* img = this; - if (m_size.min() == 0 || m_format != cudaFormat) - { - converted = new Image(max(m_size, 1), cudaFormat); - converted->set(*this); - img = converted; - arrayDesc.Width = img->getSize().x; - arrayDesc.Height = img->getSize().y; - } - - // create CUDA array. - - CudaModule::staticInit(); - - CUarray cudaArray; - CudaModule::checkError("cuArrayCreate", cuArrayCreate(&cudaArray, &arrayDesc)); - - // Upload data. - - CUDA_MEMCPY2D copyDesc; - memset(©Desc, 0, sizeof(CUDA_MEMCPY2D)); - - copyDesc.srcXInBytes = 0; - copyDesc.srcY = 0; - copyDesc.srcMemoryType = CU_MEMORYTYPE_HOST; - copyDesc.srcHost = img->getPtr(); - copyDesc.srcPitch = img->getSize().x * img->getBPP(); - copyDesc.dstXInBytes = 0; - copyDesc.dstY = 0; - copyDesc.dstMemoryType = CU_MEMORYTYPE_ARRAY; - copyDesc.dstArray = cudaArray; - copyDesc.WidthInBytes = img->getSize().x * img->getBPP(); - copyDesc.Height = img->getSize().y; - - CudaModule::checkError("cuMemcpy2D", cuMemcpy2D(©Desc)); - - // Set output parameters. - - if (formatOut) - *formatOut = cudaFormat; - - if (arrayDescOut) - *arrayDescOut = arrayDesc; - - delete converted; - return cudaArray; - -#endif -} - -//------------------------------------------------------------------------ -// TODO: Figure out a cleaner interface for this. -// TODO: Expose wrap mode, filter mode, and flags. -// TODO: Support mipmapping. - -CUtexObject Image::createCudaTexObject(ImageFormat::ID desiredFormat, ImageFormat* formatOut, CUDA_ARRAY_DESCRIPTOR* arrayDescOut, CUarray* arrayOut) const -{ -#if (!FW_USE_CUDA) - - FW_UNREF(desiredFormat); - FW_UNREF(formatOut); - FW_UNREF(arrayDescOut); - FW_UNREF(arrayOut); - fail("Image::createCudaTexObject(): Built without FW_USE_CUDA!"); - return NULL; - -#else - - // create CUDA array. - - CUDA_ARRAY_DESCRIPTOR arrayDesc; - CUarray cudaArray = createCudaArray(desiredFormat, formatOut, &arrayDesc); - - // Fill in the resource descriptor. - - CUDA_RESOURCE_DESC resDesc; - memset(&resDesc, 0, sizeof(resDesc)); - - resDesc.resType = CU_RESOURCE_TYPE_ARRAY; - resDesc.res.array.hArray = cudaArray; - resDesc.flags = 0; - - // Fill in the texture descriptor. - - CUDA_TEXTURE_DESC texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - - texDesc.addressMode[0] = CU_TR_ADDRESS_MODE_WRAP; - texDesc.addressMode[1] = CU_TR_ADDRESS_MODE_WRAP; - texDesc.addressMode[2] = CU_TR_ADDRESS_MODE_WRAP; - texDesc.filterMode = CU_TR_FILTER_MODE_LINEAR; - texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; - texDesc.maxAnisotropy = 16; - texDesc.mipmapFilterMode = CU_TR_FILTER_MODE_LINEAR; - texDesc.mipmapLevelBias = 0.0f; - texDesc.minMipmapLevelClamp = 0.0f; - texDesc.maxMipmapLevelClamp = 16.0f; - - // create CUDA texture object. - - CudaModule::staticInit(); - - CUtexObject cudaTexObject; - CudaModule::checkError("cuTexObjectCreate", cuTexObjectCreate(&cudaTexObject, &resDesc, &texDesc, NULL)); - - // Set output parameters. - - if (arrayDescOut) - *arrayDescOut = arrayDesc; - - if (arrayOut) - *arrayOut = cudaArray; - - return cudaTexObject; - -#endif -} - -//------------------------------------------------------------------------ -// Implements a polyphase filter with round-down semantics from: -// -// Non-Power-of-Two Mipmapping -// (NVIDIA whitepaper) -// http://developer.nvidia.com/object/np2_mipmapping.html - -Image* Image::downscale2x(void) const -{ - // 1x1 or smaller => Bail out. - - int area = m_size.x * m_size.y; - if (area <= 1) - return NULL; - - // Choose filter dimensions. - - int fw = (m_size.x == 1) ? 1 : ((m_size.x & 1) == 0) ? 2 : 3; - int fh = (m_size.y == 1) ? 1 : ((m_size.y & 1) == 0) ? 2 : 3; - Vec2i resSize = max(m_size >> 1, 1); - int halfArea = area >> 1; - - // Allocate temporary scanline buffer and result image. - - Image tmp(Vec2i(m_size.x, fh), ImageFormat::ABGR_8888); - Image* res = new Image(resSize, ImageFormat::ABGR_8888); - U32* resPtr = (U32*)res->getMutablePtr(); - - // Process each scanline in the result. - - for (int y = 0; y < resSize.y; y++) - { - // Copy source scanlines into the temporary buffer. - - tmp.set(0, *this, Vec2i(0, y * 2), Vec2i(m_size.x, fh)); - - // Choose weights along the Y-axis. - - Vec3i wy(resSize.y); - if (fh == 3) - wy = Vec3i(resSize.y - y, resSize.y, y + 1); - - // Process each pixel in the result. - - for (int x = 0; x < resSize.x; x++) - { - // Choose weights along the X-axis. - - Vec3i wx(resSize.x); - if (fw == 3) - wx = Vec3i(resSize.x - x, resSize.x, x + 1); - - // Compute weighted average of pixel values. - - Vec4i sum = 0; - const U32* tmpPtr = (const U32*)tmp.getPtr(Vec2i(x * 2, 0)); - - for (int yy = 0; yy < fh; yy++) - { - for (int xx = 0; xx < fw; xx++) - { - U32 abgr = tmpPtr[xx]; - int weight = wx[xx] * wy[yy]; - sum.x += (abgr & 0xFF) * weight; - sum.y += ((abgr >> 8) & 0xFF) * weight; - sum.z += ((abgr >> 16) & 0xFF) * weight; - sum.w += (abgr >> 24) * weight; - } - tmpPtr += m_size.x; - } - - sum = (sum + halfArea) / area; - *resPtr++ = sum.x | (sum.y << 8) | (sum.z << 16) | (sum.w << 24); - } - } - return res; -} - -//------------------------------------------------------------------------ - -void Image::init(const Vec2i& size, const ImageFormat& format) -{ - FW_ASSERT(size.min() >= 0); - m_size = size; - m_format = format; - m_channelTmp.resize(m_format.getNumChannels()); -} - -//------------------------------------------------------------------------ - -void Image::createBuffer(void) -{ - m_stride = m_size.x * m_format.getBPP(); - m_buffer = new Buffer; - m_buffer->resize(m_stride * m_size.y); - m_ownBuffer = true; - m_offset = 0; -} - -//------------------------------------------------------------------------ - -void Image::replicatePixel(void) -{ - if (m_size.min() == 0) - return; - - int bpp = getBPP(); - U8* ptr = getMutablePtr(); - int scanBytes = m_size.x * bpp; - - for (int x = 1; x < m_size.x; x++) - memcpy(ptr + x * bpp, ptr, bpp); - for (int y = 1; y < m_size.y; y++) - memcpy(ptr + y * m_stride, ptr, scanBytes); -} - -//------------------------------------------------------------------------ - -bool Image::canBlitDirectly(const ImageFormat& format) -{ - switch (format.getID()) - { - case ImageFormat::R8_G8_B8: return true; - case ImageFormat::R8_G8_B8_A8: return true; - case ImageFormat::A8: return true; - case ImageFormat::XBGR_8888: return true; - case ImageFormat::ABGR_8888: return true; - - case ImageFormat::RGB_565: return true; - case ImageFormat::RGBA_5551: return true; - - case ImageFormat::RGB_Vec3f: return true; - case ImageFormat::RGBA_Vec4f: return true; - case ImageFormat::A_F32: return true; - - default: return false; - } -} - -//------------------------------------------------------------------------ - -bool Image::canBlitThruABGR(const ImageFormat& format) -{ - switch (format.getID()) - { - case ImageFormat::R8_G8_B8: return true; - case ImageFormat::R8_G8_B8_A8: return true; - case ImageFormat::A8: return true; - case ImageFormat::XBGR_8888: return true; - case ImageFormat::ABGR_8888: return true; - - case ImageFormat::RGB_565: return true; - case ImageFormat::RGBA_5551: return true; - - case ImageFormat::RGB_Vec3f: return false; - case ImageFormat::RGBA_Vec4f: return false; - case ImageFormat::A_F32: return false; - - default: return false; - } -} - -//------------------------------------------------------------------------ - -void Image::blit( - const ImageFormat& dstFormat, U8* dstPtr, S64 dstStride, - const ImageFormat& srcFormat, const U8* srcPtr, S64 srcStride, - const Vec2i& size) -{ - FW_ASSERT(size.min() >= 0); - if (size.min() == 0) - return; - - // Same format? - - if (dstFormat == srcFormat) - { - int scanBytes = size.x * dstFormat.getBPP(); - for (int y = 0; y < size.y; y++) - memcpy(dstPtr + dstStride * y, srcPtr + srcStride * y, scanBytes); - return; - } - - // To ABGR_8888? - - if (dstFormat.getID() == ImageFormat::ABGR_8888 && canBlitDirectly(srcFormat)) - { - for (int y = 0; y < size.y; y++) - blitToABGR((U32*)(dstPtr + dstStride * y), srcFormat, srcPtr + srcStride * y, size.x); - return; - } - - // From ABGR_8888? - - if (srcFormat.getID() == ImageFormat::ABGR_8888 && canBlitDirectly(dstFormat)) - { - for (int y = 0; y < size.y; y++) - blitFromABGR(dstFormat, dstPtr + dstStride * y, (const U32*)(srcPtr + srcStride * y), size.x); - return; - } - - // From integer-based format to another => convert thru ABGR_8888. - - if (canBlitDirectly(srcFormat) && canBlitDirectly(dstFormat) && canBlitThruABGR(srcFormat)) - { - Array tmp(NULL, size.x); - for (int y = 0; y < size.y; y++) - { - blitToABGR(tmp.getPtr(), srcFormat, srcPtr + srcStride * y, size.x); - blitFromABGR(dstFormat, dstPtr + dstStride * y, tmp.getPtr(), size.x); - } - return; - } - - // General case. - - S64 dstBPP = dstFormat.getBPP(); - S64 srcBPP = srcFormat.getBPP(); - Array dv(NULL, dstFormat.getNumChannels()); - Array sv(NULL, srcFormat.getNumChannels()); - Array map; - - for (int i = 0; i < dstFormat.getNumChannels(); i++) - { - ImageFormat::ChannelType t = dstFormat.getChannel(i).Type; - dv[i] = (t == ImageFormat::ChannelType_A) ? 1.0f : 0.0f; - int si = srcFormat.findChannel(t); - if (si != -1) - map.add(Vec2i(i, si)); - } - - for (int y = 0; y < size.y; y++) - { - U8* dstPixel = dstPtr + dstStride * y; - const U8* srcPixel = srcPtr + srcStride * y; - - for (int x = 0; x < size.x; x++) - { - getChannels(sv.getPtr(), srcPixel, srcFormat, 0, sv.getSize()); - for (int i = 0; i < map.getSize(); i++) - dv[map[i].x] = sv[map[i].y]; - setChannels(dstPixel, dv.getPtr(), dstFormat, 0, dv.getSize()); - - dstPixel += dstBPP; - srcPixel += srcBPP; - } - } -} - -//------------------------------------------------------------------------ - -void Image::blitToABGR(U32* dstPtr, const ImageFormat& srcFormat, const U8* srcPtr, int width) -{ - FW_ASSERT(width > 0); - FW_ASSERT(dstPtr && srcPtr); - FW_ASSERT(canBlitDirectly(srcFormat)); - - const U8* s8 = srcPtr; - const U16* s16 = (const U16*)srcPtr; - const Vec3f* sv3 = (const Vec3f*)srcPtr; - const Vec4f* sv4 = (const Vec4f*)srcPtr; - const F32* sf = (const F32*)srcPtr; - - switch (srcFormat.getID()) - { - case ImageFormat::R8_G8_B8: for (int x = width; x > 0; x--) { *dstPtr++ = s8[0] | (s8[1] << 8) | (s8[2] << 16) | 0xFF000000; s8 += 3; } break; - case ImageFormat::R8_G8_B8_A8: for (int x = width; x > 0; x--) { *dstPtr++ = s8[0] | (s8[1] << 8) | (s8[2] << 16) | (s8[3] << 24); s8 += 4; } break; - case ImageFormat::A8: for (int x = width; x > 0; x--) *dstPtr++ = *s8++ << 24; break; - case ImageFormat::XBGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - case ImageFormat::ABGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - - case ImageFormat::RGB_565: for (int x = width; x > 0; x--) { U16 v = *s16++; *dstPtr++ = RGB_565_TO_ABGR_8888(v); } break; - case ImageFormat::RGBA_5551: for (int x = width; x > 0; x--) { U16 v = *s16++; *dstPtr++ = RGBA_5551_TO_ABGR_8888(v); } break; - - case ImageFormat::RGB_Vec3f: for (int x = width; x > 0; x--) *dstPtr++ = Vec4f(*sv3++, 1.0f).toABGR(); break; - case ImageFormat::RGBA_Vec4f: for (int x = width; x > 0; x--) *dstPtr++ = (sv4++)->toABGR(); break; - case ImageFormat::A_F32: for (int x = width; x > 0; x--) *dstPtr++ = clamp((int)(*sf++ * 255.0f + 0.5f), 0x00, 0xFF) << 24; break; - - default: FW_ASSERT(false); break; - } -} - -//------------------------------------------------------------------------ - -void Image::blitFromABGR(const ImageFormat& dstFormat, U8* dstPtr, const U32* srcPtr, int width) -{ - FW_ASSERT(width > 0); - FW_ASSERT(dstPtr && srcPtr); - FW_ASSERT(canBlitDirectly(dstFormat)); - - U8* d8 = dstPtr; - U16* d16 = (U16*)dstPtr; - Vec3f* dv3 = (Vec3f*)dstPtr; - Vec4f* dv4 = (Vec4f*)dstPtr; - F32* df = (F32*)dstPtr; - - switch (dstFormat.getID()) - { - case ImageFormat::R8_G8_B8: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d8++ = (U8)v; *d8++ = (U8)(v >> 8); *d8++ = (U8)(v >> 16); } break; - case ImageFormat::R8_G8_B8_A8: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d8++ = (U8)v; *d8++ = (U8)(v >> 8); *d8++ = (U8)(v >> 16); *d8++ = (U8)(v >> 24); } break; - case ImageFormat::A8: for (int x = width; x > 0; x--) *d8++ = (U8)(*srcPtr++ >> 24); break; - case ImageFormat::XBGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - case ImageFormat::ABGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - - case ImageFormat::RGB_565: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d16++ = ABGR_8888_TO_RGB_565(v); } break; - case ImageFormat::RGBA_5551: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d16++ = ABGR_8888_TO_RGBA_5551(v); } break; - - case ImageFormat::RGB_Vec3f: for (int x = width; x > 0; x--) *dv3++ = Vec4f::fromABGR(*srcPtr++).getXYZ(); break; - case ImageFormat::RGBA_Vec4f: for (int x = width; x > 0; x--) *dv4++ = Vec4f::fromABGR(*srcPtr++); break; - case ImageFormat::A_F32: for (int x = width; x > 0; x--) *df++ = (F32)(*srcPtr++ >> 24) / 255.0f; break; - - default: FW_ASSERT(false); break; - } -} - -//------------------------------------------------------------------------ - -void Image::getChannels(F32* values, const U8* pixelPtr, const ImageFormat& format, int first, int num) -{ - FW_ASSERT(num >= 0); - FW_ASSERT((values && pixelPtr) || !num); - FW_ASSERT(first >= 0 && first + num <= format.getNumChannels()); - - for (int i = 0; i < num; i++) - { - const ImageFormat::Channel& c = format.getChannel(i + first); - const U8* wordPtr = pixelPtr + c.wordOfs; - U32 field; - switch (c.wordSize) - { - case 1: field = *wordPtr; break; - case 2: field = *(const U16*)wordPtr; break; - case 4: field = *(const U32*)wordPtr; break; - default: FW_ASSERT(false); return; - } - field >>= c.fieldOfs; - - U32 mask = (1 << c.fieldSize) - 1; - switch (c.format) - { - case ImageFormat::ChannelFormat_Clamp: values[i] = (F32)(field & mask) / (F32)mask; break; - case ImageFormat::ChannelFormat_Int: values[i] = (F32)(field & mask); break; - case ImageFormat::ChannelFormat_Float: FW_ASSERT(c.fieldSize == 32); values[i] = bitsToFloat(field); break; - default: FW_ASSERT(false); return; - } - } -} - -//------------------------------------------------------------------------ - -void Image::setChannels(U8* pixelPtr, const F32* values, const ImageFormat& format, int first, int num) -{ - FW_ASSERT(num >= 0); - FW_ASSERT((pixelPtr && values) || !num); - FW_ASSERT(first >= 0 && first + num <= format.getNumChannels()); - - memset(pixelPtr, 0, format.getBPP()); - - for (int i = 0; i < num; i++) - { - const ImageFormat::Channel& c = format.getChannel(i + first); - U32 mask = (1 << c.fieldSize) - 1; - U32 field; - switch (c.format) - { - case ImageFormat::ChannelFormat_Clamp: field = min((U32)max(values[i] * (F32)mask + 0.5f, 0.0f), mask); break; - case ImageFormat::ChannelFormat_Int: field = min((U32)max(values[i] + 0.5f, 0.0f), mask); break; - case ImageFormat::ChannelFormat_Float: FW_ASSERT(c.fieldSize == 32); field = floatToBits(values[i]); break; - default: FW_ASSERT(false); return; - } - field <<= c.fieldOfs; - - U8* wordPtr = pixelPtr + c.wordOfs; - switch (c.wordSize) - { - case 1: *wordPtr |= (U8)field; break; - case 2: *(U16*)wordPtr |= (U16)field; break; - case 4: *(U32*)wordPtr |= field; break; - default: FW_ASSERT(false); return; - } - } -} - -//------------------------------------------------------------------------ -#endif \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryImage.hpp b/Framework/Source/Graphics/Model/Loaders/BinaryImage.hpp deleted file mode 100644 index 9d786a224..000000000 --- a/Framework/Source/Graphics/Model/Loaders/BinaryImage.hpp +++ /dev/null @@ -1,251 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -// #include "gpu/Buffer.hpp" -// #include "io/FileFormats.hpp" -#include -using namespace Falcor; - -/** Mostly deprecated file although ImageFormat is currently still used in BinaryModelExporter. - You may have been looking for Bitmap.h. -*/ -namespace FW -{ -//------------------------------------------------------------------------ - using S32 = int32_t; - using U32 = uint32_t; - using U8 = uint8_t; -#define FW_ASSERT assert -#define FW_ARRAY_SIZE arraysize - -class ImageFormat -{ -public: - enum ID - { - R8_G8_B8 = 0, - R8_G8_B8_A8, - A8, - XBGR_8888, - ABGR_8888, - - RGB_565, - RGBA_5551, - - RGB_Vec3f, - RGBA_Vec4f, - A_F32, - - BGRA_8888, - BGR_888, - RG_88, - R8, - - // Compressed formats - S3TC_DXT1, - S3TC_DXT3, - S3TC_DXT5, - RGTC_R, - RGTC_RG, - - ID_Generic, - ID_Max - }; - - enum ChannelType // allows arbitrary values - { - ChannelType_R = 0, - ChannelType_G, - ChannelType_B, - ChannelType_A, - ChannelType_Generic, - - ChannelType_Max - }; - - enum ChannelFormat - { - ChannelFormat_Clamp = 0, // [0, 1] - ChannelFormat_Int, // [0, n[ - ChannelFormat_Float, // any - - ChannelFormat_Max - }; - - struct Channel - { - ChannelType Type; - ChannelFormat format; - S32 wordOfs; // bytes - S32 wordSize; // bytes - S32 fieldOfs; // bits - S32 fieldSize; // bits - }; - - struct StaticFormat - { - S32 bpp; - S32 numChannels; - Channel channels[4]; -// GLenum glInternalFormat; -// GLenum glFormat; -// GLenum glType; -// bool glLittleEndian; - }; - -public: - ImageFormat (void) { clear(); } - ImageFormat (ID id) : m_id(id) { FW_ASSERT(id >= 0 && id < ID_Generic); } - ImageFormat (const ImageFormat& other) { set(other); } - ~ImageFormat (void) {} - - ID getID (void) const; - const StaticFormat* getStaticFormat (void) const; - int getBPP (void) const; - int getNumChannels (void) const; - const Channel& getChannel (int idx) const; - int findChannel (ChannelType Type) const; - bool hasChannel (ChannelType Type) const { return (findChannel(Type) != -1); } - - void set (const ImageFormat& other); - void clear (void); - void addChannel (const Channel& channel); - - ID getGLFormat (void) const; - - ImageFormat& operator= (const ImageFormat& other) { set(other); return *this; } - bool operator== (const ImageFormat& other) const; - bool operator!= (const ImageFormat& other) const { return (!operator==(other)); } - -private: - static const StaticFormat s_staticFormats[]; - - mutable ID m_id; // ID_Max if unknown - S32 m_genericBPP; // only if m_id >= ID_Generic - std::vector m_genericChannels; // only if m_id >= ID_Generic -}; - -//------------------------------------------------------------------------ -#if 0 -class Image -{ -public: - Image (const Vec2i& size, const ImageFormat& format = ImageFormat::ABGR_8888) { init(size, format); createBuffer(); } - Image (const Vec2i& size, const ImageFormat& format, void* ptr, S64 stride); - Image (const Vec2i& size, const ImageFormat& format, Buffer& buffer, S64 ofs, S64 stride); - Image (const Image& other) { init(other.getSize(), other.getFormat()); createBuffer(); set(other); } - ~Image (void); - - bool contains (const Vec2i& pos, const Vec2i& size) const { return (pos.x >= 0 && pos.y >= 0 && pos.x + size.x <= m_size.x && pos.y + size.y <= m_size.y); } - - const Vec2i& getSize (void) const { return m_size; } - const ImageFormat& getFormat (void) const { return m_format; } - int getBPP (void) const { return m_format.getBPP(); } - S64 getStride (void) const { return m_stride; } - - Buffer& getBuffer (void) const { return *m_buffer; } - S64 getOffset (const Vec2i& pos = 0) const { FW_ASSERT(contains(pos, 0)); return m_offset + pos.x * getBPP() + pos.y * getStride(); } - const U8* getPtr (const Vec2i& pos = 0) const { return (const U8*)m_buffer->getPtr(getOffset(pos)); } - U8* getMutablePtr (const Vec2i& pos = 0) { return (U8*)m_buffer->getMutablePtr(getOffset(pos)); } - - void read (const ImageFormat& format, void* ptr, S64 stride, const Vec2i& pos, const Vec2i& size) const { FW_ASSERT(contains(pos, size)); blit(format, (U8*)ptr, stride, getFormat(), getPtr(pos), getStride(), size); } - void read (const ImageFormat& format, void* ptr, S64 stride) const { blit(format, (U8*)ptr, stride, getFormat(), getPtr(), getStride(), getSize()); } - void write (const ImageFormat& format, const void* ptr, S64 stride, const Vec2i& pos, const Vec2i& size) { FW_ASSERT(contains(pos, size)); blit(getFormat(), getMutablePtr(pos), getStride(), format, (const U8*)ptr, stride, size); } - void write (const ImageFormat& format, const void* ptr, S64 stride) { blit(getFormat(), getMutablePtr(), getStride(), format, (const U8*)ptr, stride, getSize()); } - void set (const Vec2i& dstPos, const Image& src, const Vec2i& srcPos, const Vec2i& size) { FW_ASSERT(contains(dstPos, size) && src.contains(srcPos, size)); blit(getFormat(), getMutablePtr(dstPos), getStride(), src.getFormat(), src.getPtr(srcPos), src.getStride(), size); } - void set (const Image& src) { blit(getFormat(), getMutablePtr(), getStride(), src.getFormat(), src.getPtr(), src.getStride(), Vec2i(min(getSize().x, src.getSize().x), min(getSize().y, src.getSize().y))); } - - void clear (U32 abgr = 0) { if (m_size.min() != 0) setABGR(0, abgr); replicatePixel(); } - void clear (const Vec4f& color) { if (m_size.min() != 0) setVec4f(0, color); replicatePixel(); } - - U32 getABGR (const Vec2i& pos) const; - void setABGR (const Vec2i& pos, U32 value); - Vec4f getVec4f (const Vec2i& pos) const; - void setVec4f (const Vec2i& pos, const Vec4f& value); - Vec3f getVec3f (const Vec2i& pos) const { return getVec4f(pos).getXYZ(); } - void setVec3f (const Vec2i& pos, const Vec3f& value) { setVec4f(pos, Vec4f(value, 1.0f)); } - - U32 getABGR (int x, int y) const { return getABGR(Vec2i(x, y)); } - void setABGR (int x, int y, U32 value) { setABGR(Vec2i(x, y), value); } - Vec4f getVec4f (int x, int y) const { return getVec4f(Vec2i(x, y)); } - void setVec4f (int x, int y, const Vec4f& value) { setVec4f(Vec2i(x, y), value); } - Vec3f getVec3f (int x, int y) const { return getVec3f(Vec2i(x, y)); } - void setVec3f (int x, int y, const Vec3f& value) { setVec3f(Vec2i(x, y), value); } - - void getChannels (F32* values, const Vec2i& pos, int first, int num) const { getChannels(values, getPtr(pos), getFormat(), first, num); } - void getChannels (F32* values, const Vec2i& pos) const { getChannels(values, getPtr(pos), getFormat(), 0, getFormat().getNumChannels()); } - const Array& getChannels (const Vec2i& pos) const { getChannels(m_channelTmp.getPtr(), getPtr(pos), getFormat(), 0, getFormat().getNumChannels()); return m_channelTmp; } - F32 getChannel (const Vec2i& pos, int idx) const { F32 res; getChannels(&res, getPtr(pos), getFormat(), idx, 1); return res; } - void setChannels (const Vec2i& pos, const F32* values, int first, int num) { setChannels(getMutablePtr(pos), values, getFormat(), first, num); } - void setChannels (const Vec2i& pos, const F32* values) { setChannels(getMutablePtr(pos), values, getFormat(), 0, getFormat().getNumChannels()); } - void setChannel (const Vec2i& pos, int idx, F32 value) { setChannels(getMutablePtr(pos), &value, getFormat(), idx, 1); } - - void flipX (void); - void flipY (void); - - GLuint createGLTexture (ImageFormat::ID desiredFormat = ImageFormat::ID_Max, bool generateMipmaps = true) const; - - ImageFormat chooseCudaFormat(CUDA_ARRAY_DESCRIPTOR* desc = NULL, ImageFormat::ID desiredFormat = ImageFormat::ID_Max) const; - CUarray createCudaArray (ImageFormat::ID desiredFormat = ImageFormat::ID_Max, ImageFormat* formatOut = NULL, CUDA_ARRAY_DESCRIPTOR* arrayDescOut = NULL) const; - CUtexObject createCudaTexObject(ImageFormat::ID desiredFormat = ImageFormat::ID_Max, ImageFormat* formatOut = NULL, CUDA_ARRAY_DESCRIPTOR* arrayDescOut = NULL, CUarray* arrayOut = NULL) const; - - Image* downscale2x (void) const; // Returns ImageFormat::ABGR_8888, or NULL if size <= 1x1. - - Image& operator= (const Image& other) { if (&other != this) set(other); return *this; } - -private: - void init (const Vec2i& size, const ImageFormat& format); - void createBuffer (void); - void replicatePixel (void); - - static bool canBlitDirectly (const ImageFormat& format); - static bool canBlitThruABGR (const ImageFormat& format); - - static void blit (const ImageFormat& dstFormat, U8* dstPtr, S64 dstStride, - const ImageFormat& srcFormat, const U8* srcPtr, S64 srcStride, - const Vec2i& size); - - static void blitToABGR (U32* dstPtr, const ImageFormat& srcFormat, const U8* srcPtr, int width); - static void blitFromABGR (const ImageFormat& dstFormat, U8* dstPtr, const U32* srcPtr, int width); - - static void getChannels (F32* values, const U8* pixelPtr, const ImageFormat& format, int first, int num); - static void setChannels (U8* pixelPtr, const F32* values, const ImageFormat& format, int first, int num); - -private: - Vec2i m_size; - ImageFormat m_format; - S64 m_stride; - Buffer* m_buffer; - bool m_ownBuffer; - S64 m_offset; - - mutable Array m_channelTmp; -}; -#endif -//------------------------------------------------------------------------ -} diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.cpp b/Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.cpp deleted file mode 100644 index ddf65bd5f..000000000 --- a/Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "BinaryModelExporter.h" -#include "../Model.h" -#include "../Mesh.h" -#include "API/VAO.h" -#include "BinaryModelSpec.h" -#include "API/Buffer.h" -#include "API/Texture.h" -#include "BinaryImage.hpp" -#include "Data/VertexAttrib.h" -#include "API/Device.h" - -namespace Falcor -{ - static FW::ImageFormat::ID getBinaryFormatID(ResourceFormat format) - { - switch(format) - { - case ResourceFormat::RGBA8Unorm: - case ResourceFormat::RGBA8UnormSrgb: - return FW::ImageFormat::R8_G8_B8_A8; - case ResourceFormat::Alpha8Unorm: - return FW::ImageFormat::A8; - case ResourceFormat::R5G6B5Unorm: - return FW::ImageFormat::RGB_565; - case ResourceFormat::RGB5A1Unorm: - return FW::ImageFormat::RGBA_5551; - case ResourceFormat::RGB32Float: - return FW::ImageFormat::RGB_Vec3f; - case ResourceFormat::RGBA32Float: - return FW::ImageFormat::RGBA_Vec4f; - case ResourceFormat::Alpha32Float: - return FW::ImageFormat::A_F32; - - case ResourceFormat::BGRA8Unorm: - case ResourceFormat::BGRA8UnormSrgb: - return FW::ImageFormat::BGRA_8888; - case ResourceFormat::RG8Unorm: - return FW::ImageFormat::RG_88; - case ResourceFormat::R8Unorm: - return FW::ImageFormat::R8; - - case ResourceFormat::BC1Unorm: - case ResourceFormat::BC1UnormSrgb: - return FW::ImageFormat::S3TC_DXT1; - case ResourceFormat::BC2Unorm: - case ResourceFormat::BC2UnormSrgb: - return FW::ImageFormat::S3TC_DXT3; - case ResourceFormat::BC3Unorm: - case ResourceFormat::BC3UnormSrgb: - return FW::ImageFormat::S3TC_DXT5; - case ResourceFormat::BC4Unorm: - return FW::ImageFormat::RGTC_R; - case ResourceFormat::BC5Unorm: - return FW::ImageFormat::RGTC_RG; - default: - should_not_get_here(); - return FW::ImageFormat::ID_Max; - } - } - - static Texture::SharedPtr getTexture(Material::SharedPtr pMaterial, TextureType map) - { - switch(map) - { - case TextureType_Diffuse: - return pMaterial->getBaseColorTexture(); - case TextureType_Normal: - return pMaterial->getNormalMap(); - case TextureType_Specular: - return pMaterial->getSpecularTexture(); - case TextureType_Displacement: - return pMaterial->getHeightMap(); - default: - return nullptr; - } - } - - static AttribType getBinaryAttribType(const std::string& name) - { - if(name == VERTEX_POSITION_NAME) - return AttribType_Position; - else if(name == VERTEX_NORMAL_NAME) - return AttribType_Normal; - else if(name == VERTEX_BITANGENT_NAME) - return AttribType_Bitangent; - else if(name == VERTEX_DIFFUSE_COLOR_NAME) - return AttribType_Color; - else if(name == VERTEX_TEXCOORD_NAME) - return AttribType_TexCoord; - - should_not_get_here(); - return AttribType_Max; - } - - static AttribFormat GetBinaryAttribFormat(ResourceFormat format) - { - switch(format) - { - case ResourceFormat::R8Unorm: - case ResourceFormat::RG8Unorm: - case ResourceFormat::RGBA8Unorm: - return AttribFormat_U8; - case ResourceFormat::R32Int: - case ResourceFormat::RG32Int: - case ResourceFormat::RGB32Int: - case ResourceFormat::RGBA32Int: - return AttribFormat_S32; - case ResourceFormat::R32Float: - case ResourceFormat::RG32Float: - case ResourceFormat::RGB32Float: - case ResourceFormat::RGBA32Float: - return AttribFormat_F32; - default: - should_not_get_here(); // Format not supported by the binary file - return AttribFormat_Max; - } - } - - void writeString(BinaryFileStream& stream, const std::string& str) - { - stream << (int32_t)str.size(); - stream.write(str.c_str(), str.size());; - } - - void BinaryModelExporter::exportToFile(const std::string& filename, const Model* pModel) - { - BinaryModelExporter(filename, pModel); - } - - void BinaryModelExporter::error(const std::string& msg) - { - logError("Error when exporting model \"" + mFilename + "\".\n" + msg); - mStream.remove(); - } - - void BinaryModelExporter::warning(const std::string& Msg) - { - logError("Warning when exporting model \"" + mFilename + "\".\n" + Msg); - } - - BinaryModelExporter::BinaryModelExporter(const std::string& filename, const Model* pModel) : mFilename(filename) - { - mStream.open(filename.c_str(), BinaryFileStream::Mode::Write); - mpModel = pModel; - - if(mpModel->hasBones()) - { - error("Binary format doesn't support model with bones"); - return; - } - - if(mpModel->hasAnimations()) - { - error("Binary format doesn't support model with animations"); - return; - } - - if(prepareSubmeshes() == false) return; - if(writeHeader() == false) return; - if(writeTextures() == false) return; - if(writeMeshes() == false) return; - if(writeInstances() == false) return; - } - - bool BinaryModelExporter::prepareSubmeshes() - { - // The binary format has a concept of submeshes, that share the same vertex buffer, but have different materials and index buffers. - // Model works in a similar way (meshes can share VB), but only stores the meshes vector. We need to process that vector to identify submeshes. - for(uint32_t i = 0; i < mpModel->getMeshCount(); i++) - { - const auto& pMesh = mpModel->getMesh(i); - if(pMesh->getVao()->getPrimitiveTopology() != Vao::Topology::TriangleList) - { - warning("Binary format doesn't support topologies other than triangles."); - continue; - } - - const auto& pVao = pMesh->getVao(); - auto& submesh = mMeshes[pVao.get()]; - submesh.push_back(i); - } - - // Calculate the number of mesh instances - for(const auto& m : mMeshes) - { - mInstanceCount += mpModel->getMeshInstanceCount(m.second[0]); - } - - return true; - } - - bool BinaryModelExporter::writeHeader() - { - mStream.write("BinScene", 8); - mStream << (int32_t)8 << (int32_t)mpModel->getTextureCount() << (int32_t)mMeshes.size() << (int32_t)mInstanceCount; - return true; - } - - bool BinaryModelExporter::writeTextures() - { - mTextureHash[nullptr] = -1; - - uint32_t texID = 0; - for (uint32_t meshID = 0; meshID < mpModel->getMeshCount(); meshID++) - { - bool succeeded = true; - - // Write all material textures - const auto& pMaterial = mpModel->getMesh(meshID)->getMaterial(); - succeeded &= writeMaterialTexture(texID, pMaterial->getBaseColorTexture()); - succeeded &= writeMaterialTexture(texID, pMaterial->getSpecularTexture()); - succeeded &= writeMaterialTexture(texID, pMaterial->getEmissiveTexture()); - succeeded &= writeMaterialTexture(texID, pMaterial->getNormalMap()); - succeeded &= writeMaterialTexture(texID, pMaterial->getOcclusionMap()); - succeeded &= writeMaterialTexture(texID, pMaterial->getLightMap()); - succeeded &= writeMaterialTexture(texID, pMaterial->getHeightMap()); - - if (succeeded == false) - { - return false; - } - } - - return true; - } - - bool BinaryModelExporter::writeCommonMeshData(const Mesh::SharedPtr& pMesh, uint32_t submeshCount) - { - auto pVao = pMesh->getVao(); - const uint32_t vertexBufferCount = pMesh->getVao()->getVertexBuffersCount(); - mStream << (int32_t)vertexBufferCount << (int32_t)pMesh->getVertexCount() << (int32_t)submeshCount; - - struct vertexBufferInfo - { - Buffer::SharedPtr pBuffer; - size_t pData; //this had the p flag because it represents the pointer to the vertex buffers data - uint32_t stride; - }; - - std::vector vbInfo(vertexBufferCount); - - for (uint32_t i = 0; i < vertexBufferCount; i++) - { - const VertexBufferLayout* pLayout = pVao->getVertexLayout()->getBufferLayout(i).get(); - assert(pLayout->getElementCount() == 1); - AttribType type = getBinaryAttribType(pLayout->getElementName(0)); - AttribFormat format = GetBinaryAttribFormat(pLayout->getElementFormat(0)); - uint32_t channels = getFormatChannelCount(pLayout->getElementFormat(0)); - - if(type == AttribType_Max) - { - error("Unsupported attribute Type"); - return false; - } - - if(format == AttribFormat_Max) - { - error("Unsupported attribute format"); - return false; - } - mStream << (int32_t)type << (int32_t)format << (int32_t)channels; - - vbInfo[i].pBuffer = pVao->getVertexBuffer(i); - vbInfo[i].stride = pLayout->getStride(); - vbInfo[i].pData = (size_t)vbInfo[i].pBuffer->map(Buffer::MapType::Read); - } - - // Write the vertex buffer - for (uint32_t i = 0; i < pMesh->getVertexCount(); ++i) - { - for (auto& a : vbInfo) - { - mStream.write((void*)a.pData, a.stride); - a.pData += a.stride; - } - } - - for (auto& a : vbInfo) - { - a.pBuffer->unmap(); - } - - return true; - } - - bool BinaryModelExporter::writeSubmesh(const Mesh::SharedPtr& pMesh) - { - const auto pMaterial = pMesh->getMaterial(); - - glm::vec3 ambient(0,0,0); - glm::vec4 diffuse = pMaterial->getBaseColor(); - glm::vec3 specular = vec3(pMaterial->getSpecularParams()); - float glossiness = pMaterial->getSpecularParams().a; - - mStream << ambient << diffuse << specular << glossiness; - - float displacementCoeff = pMaterial->getHeightScale(); - float displacementBias = pMaterial->getHeightOffset(); - - mStream << displacementCoeff << displacementBias; - - for(uint32_t i = 0; i < TextureType_Max; i++) - { - Texture::SharedPtr pTexture = getTexture(pMaterial, TextureType(i)); - uint32_t index = -1; - if(pTexture) - { - index = mTextureHash[pTexture.get()]; - } - - mStream << index; - } - - uint32_t indexCount = pMesh->getIndexCount(); - assert(indexCount % 3 == 0); - uint32_t primCount = indexCount / 3; - - mStream << (int32_t)primCount; - - // Output the index buffer - const void* pIndices = pMesh->getVao()->getIndexBuffer()->map(Buffer::MapType::Read); - mStream.write(pIndices, indexCount * sizeof(uint32_t)); - pMesh->getVao()->getIndexBuffer()->unmap(); - - return true; - } - - bool BinaryModelExporter::writeMeshes() - { - uint32_t inst = 0; - for(const auto& mesh : mMeshes) - { - const auto& submeshes = mesh.second; - - for(uint32_t meshID : submeshes) - { - const Mesh::SharedPtr& pMesh = mpModel->getMesh(meshID); - - if(meshID == submeshes[0]) - { - // All submeshes share the same VB and same layout. We use the first submesh for that. - if(writeCommonMeshData(pMesh, (uint32_t)submeshes.size()) == false) - { - return false; - } - } - - if(writeSubmesh(pMesh) == false) - { - return false; - } - inst += mpModel->getMeshInstanceCount(meshID); - } - } - - return true; - } - - bool BinaryModelExporter::writeInstances() - { - int32_t meshIdx = 0; - int32_t enabled = 1; - for(const auto& mesh : mMeshes) - { - const uint32_t meshID = mesh.second[0]; - - for(uint32_t i = 0; i < mpModel->getMeshInstanceCount(meshID); i++) - { - glm::mat4 transformation = mpModel->getMeshInstance(meshID, i)->getTransformMatrix(); - mStream << meshIdx << enabled << transformation; - writeString(mStream, ""); // Name - writeString(mStream, ""); // Meta-data - } - - meshIdx++; - } - return true; - } - - bool BinaryModelExporter::writeMaterialTexture(uint32_t& texID, const Texture::SharedPtr& pTexture) - { - if (pTexture != nullptr) - { - // If not exported yet - if (mTextureHash.find(pTexture.get()) == mTextureHash.end()) - { - mTextureHash[pTexture.get()] = texID++; - return exportBinaryImage(pTexture.get()); - } - } - - return true; - } - - bool BinaryModelExporter::exportBinaryImage(const Texture* pTexture) - { - if(pTexture->getArraySize() > 1) - { - error("Binary file format doesn't support texture arrays."); - return false; - } - - if(pTexture->getType() != Texture::Type::Texture2D) - { - error("Binary file format only supports 2D textures."); - return false; - } - - uint32_t width = pTexture->getWidth(); - uint32_t height = pTexture->getHeight(); - ResourceFormat format = pTexture->getFormat(); - uint32_t bpp = getFormatBytesPerBlock(format); - - int32_t formatID = getBinaryFormatID(pTexture->getFormat()); - - // Write the data - std::vector data = gpDevice->getRenderContext()->readTextureSubresource(pTexture, 0); - - writeString(mStream, pTexture->getSourceFilename()); - mStream.write("BinImage", 8); - // Version, width, height, bytes-per-pixel, channel count, FormatID, DataSize - mStream << (int32_t)2 << (int32_t)width << (int32_t)height << bpp << (int32_t)0 << formatID << (int32_t)data.size(); - - mStream.write(data.data(), data.size()); - return true; - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.h b/Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.h deleted file mode 100644 index f633ad8b3..000000000 --- a/Framework/Source/Graphics/Model/Loaders/BinaryModelExporter.h +++ /dev/null @@ -1,76 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "Utils/BinaryFileStream.h" -#include -#include -#include "Graphics/Model/Mesh.h" - -namespace Falcor -{ - class Model; - class Mesh; - class Vao; - class Texture; - - class BinaryModelExporter - { - public: - /** Export a model into a binary file - \param[in] filename Model's filename or full path - \param[in] pModel The model to export - */ - static void exportToFile(const std::string& filename, const Model* pModel); - - private: - BinaryModelExporter(const std::string& filename, const Model* pModel); - const Model* mpModel = nullptr; - BinaryFileStream mStream; - const std::string& mFilename; - - bool writeHeader(); - bool writeTextures(); - bool writeMeshes(); - bool writeCommonMeshData(const Mesh::SharedPtr& pMesh, uint32_t submeshCount); - bool writeSubmesh(const Mesh::SharedPtr& pMesh); - bool writeInstances(); - - bool writeMaterialTexture(uint32_t& texID, const Texture::SharedPtr& pTexture); - - bool exportBinaryImage(const Texture* pTexture); - - void error(const std::string& Msg); - void warning(const std::string& Msg); - - bool prepareSubmeshes(); - std::map> mMeshes; // Maps to meshID in model - std::map mTextureHash; - uint32_t mInstanceCount = 0; // Not the same as Model::Instance count. Model keeps the total instance count, while the binary format has a concept of meshes and submeshes, and the instance count there is the mesh instance count. - }; -} diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.cpp b/Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.cpp deleted file mode 100644 index 9e91ce2e3..000000000 --- a/Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.cpp +++ /dev/null @@ -1,980 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "BinaryModelImporter.h" -#include "BinaryModelSpec.h" -#include "../Model.h" -#include "../Mesh.h" -#include "Utils/Platform/OS.h" -#include "API/VertexLayout.h" -#include "Data/VertexAttrib.h" -#include "API/Buffer.h" -#include "BinaryImage.hpp" -#include "API/Formats.h" -#include "API/Texture.h" -#include "Graphics/Material/Material.h" -#include "API/Device.h" -#include -#include - -namespace Falcor -{ - struct TextureData - { - uint32_t width = 0; - uint32_t height = 0; - ResourceFormat format = ResourceFormat::Unknown; - std::vector data; - std::string name; - }; - - bool isSpecialFloat(float f) - { - uint32_t d = *(uint32_t*)&f; - // Check the exponent - d = (d >> 23) & 0xff; - return d == 0xff; - } - - static vec3 projectNormalToBitangent(const vec3& normal) - { - vec3 bitangent; - if (abs(normal.x) > abs(normal.y)) - { - bitangent = vec3(normal.z, 0.f, -normal.x) / length(vec2(normal.x, normal.z)); - } - else - { - bitangent = vec3(0.f, normal.z, -normal.y) / length(vec2(normal.y, normal.z)); - } - return normalize(bitangent); - } - - static bool isInvalidVec(const vec3& v) - { - return isSpecialFloat(v.x) || isSpecialFloat(v.y) || isSpecialFloat(v.z); - } - - template - void generateSubmeshTangentData( - const std::vector& indices, - uint32_t vertexCount, - const posType* vertexPosData, - const glm::vec3* vertexNormalData, - const glm::vec2* texCrdData, - uint32_t texCrdCount, - glm::vec3* bitangentData) - { - std::memset(bitangentData, 0, vertexCount * sizeof(vec3)); - - // calculate the tangent and bitangent for every face - size_t primCount = indices.size() / 3; - for(size_t primID = 0; primID < primCount; primID++) - { - struct Data - { - posType position; - glm::vec3 normal; - glm::vec2 uv; - }; - Data V[3]; - - // Get the data - for(uint32_t i = 0; i < 3; i++) - { - uint32_t index = indices[primID * 3 + i]; - V[i].position = vertexPosData[index]; - V[i].normal = vertexNormalData[index]; - V[i].uv = texCrdData ? texCrdData[index * texCrdCount] : vec2(0); - } - - // Position delta - posType posDelta[2]; - posDelta[0] = V[1].position - V[0].position; - posDelta[1] = V[2].position - V[0].position; - - // Texture offset - glm::vec2 s = V[1].uv - V[0].uv; - glm::vec2 t = V[2].uv - V[0].uv; - - glm::vec3 tangent; - glm::vec3 bitangent; - - // when t1, t2, t3 in same position in UV space, just use default UV direction. - if((s == glm::vec2(0, 0)) || (t == glm::vec2(0, 0))) - { - const glm::vec3 &normal = V[0].normal; - bitangent = projectNormalToBitangent(normal); - tangent = cross(bitangent, normal); - } - else - { - float dirCorrection = 1.0f / (s.x * t.y - s.y * t.x); - - // tangent points in the direction where to positive X axis of the texture coord's would point in model space - // bitangent's points along the positive Y axis of the texture coord's, respectively - tangent = (posDelta[0] * t.y - posDelta[1] * t.x) * dirCorrection; - bitangent = (posDelta[1] * s.x - posDelta[0] * s.y) * dirCorrection; - } - - // store for every vertex of that face - for(uint32_t i = 0; i < 3; i++) - { - // project tangent and bitangent into the plane formed by the vertex' normal - glm::vec3 localTangent = tangent - V[i].normal * (glm::dot(tangent, V[i].normal)); - localTangent = glm::normalize(localTangent); - glm::vec3 localBitangent = bitangent - V[i].normal * (glm::dot(bitangent, V[i].normal)); - localBitangent = glm::normalize(localBitangent); - localBitangent = localBitangent - localTangent * (glm::dot(localBitangent, localTangent)); - localBitangent = glm::normalize(localBitangent); - - if (isInvalidVec(bitangent) == false) - { - // and write it into the mesh - uint32_t index = indices[primID * 3 + i]; - bitangentData[index] += normalize(localBitangent); - } - } - } - - for (uint32_t v = 0; v < vertexCount; v++) - { - bitangentData[v] = normalize(bitangentData[v]); - if (isInvalidVec(bitangentData[v])) - { - bitangentData[v] = projectNormalToBitangent(vertexNormalData[v]); - } - } - } - - static void setTexture(Material* pMaterial, Texture::SharedPtr pTexture, TextureType texType, const std::string& modelName) - { - switch(texType) - { - case TextureType_Diffuse: - pMaterial->setBaseColorTexture(pTexture); - break; - case TextureType_Normal: - pMaterial->setNormalMap(pTexture); - break; - case TextureType_Specular: - pMaterial->setSpecularTexture(pTexture); - break; - case TextureType_Displacement: - pMaterial->setHeightMap(pTexture); - break; - default: - logWarning("Texture of Type " + std::to_string(texType) + " is not supported by the material system (model " + modelName + ")"); - } - - } - - static const std::string getSemanticName(AttribType type) - { - switch(type) - { - case AttribType_Position: - return VERTEX_POSITION_NAME; - case AttribType_Normal: - return VERTEX_NORMAL_NAME; - case AttribType_Color: - return VERTEX_DIFFUSE_COLOR_NAME; - case AttribType_TexCoord: - return VERTEX_TEXCOORD_NAME; - case AttribType_Bitangent: - return VERTEX_BITANGENT_NAME; - case AttribType_Tangent: - return "unused"; - default: - should_not_get_here(); - return "unused"; - } - } - - static ResourceFormat getFalcorFormat(AttribFormat format, uint32_t components) - { - switch(format) - { - case AttribFormat_U8: - switch(components) - { - case 1: - return ResourceFormat::R8Unorm; - case 2: - return ResourceFormat::RG8Unorm; - case 3: - return ResourceFormat::RGBA8Unorm; - case 4: - return ResourceFormat::RGBA8Unorm; - } - break; - case AttribFormat_S32: - switch(components) - { - case 1: - return ResourceFormat::R32Int; - case 2: - return ResourceFormat::RG32Int; - case 3: - return ResourceFormat::RGB32Int; - case 4: - return ResourceFormat::RGBA32Int; - } - break; - case AttribFormat_F32: - switch(components) - { - case 1: - return ResourceFormat::R32Float; - case 2: - return ResourceFormat::RG32Float; - case 3: - return ResourceFormat::RGB32Float; - case 4: - return ResourceFormat::RGBA32Float; - } - break; - } - should_not_get_here(); - return ResourceFormat::Unknown; - } - - static const uint32_t kUnusedShaderElement = -1; - static uint32_t getShaderLocation(AttribType type) - { - switch(type) - { - case AttribType_Position: - return VERTEX_POSITION_LOC; - case AttribType_Normal: - return VERTEX_NORMAL_LOC; - case AttribType_Color: - return VERTEX_DIFFUSE_COLOR_LOC; - case AttribType_TexCoord: - return VERTEX_TEXCOORD_LOC; - case AttribType_Bitangent: - return VERTEX_BITANGENT_LOC; - case AttribType_Tangent: - return kUnusedShaderElement; - default: - should_not_get_here(); - return kUnusedShaderElement; - } - } - - static uint32_t getFormatByteSize(AttribFormat format) - { - switch(format) - { - case AttribFormat_U8: - return 1; - case AttribFormat_S32: - case AttribFormat_F32: - return 4; - default: - should_not_get_here(); - return 0; - } - } - - static ResourceFormat getTextureFormat(FW::ImageFormat::ID formatId) - { - // Note: If you make changes here, make sure to update GetFormatFromMapType() - switch(formatId) - { - case FW::ImageFormat::R8_G8_B8: - case FW::ImageFormat::R8_G8_B8_A8: - return ResourceFormat::RGBA8Unorm; - case FW::ImageFormat::A8: - return ResourceFormat::Alpha8Unorm; - case FW::ImageFormat::XBGR_8888: - case FW::ImageFormat::ABGR_8888: - return ResourceFormat::RGBA8Unorm; - case FW::ImageFormat::RGB_565: - return ResourceFormat::R5G6B5Unorm; - case FW::ImageFormat::RGBA_5551: - return ResourceFormat::RGB5A1Unorm; - case FW::ImageFormat::RGB_Vec3f: - return ResourceFormat::RGB32Float; - case FW::ImageFormat::RGBA_Vec4f: - return ResourceFormat::RGBA32Float; - case FW::ImageFormat::A_F32: - return ResourceFormat::Alpha32Float; - - case FW::ImageFormat::BGRA_8888: - return ResourceFormat::BGRA8Unorm; - case FW::ImageFormat::BGR_888: - return ResourceFormat::BGRA8Unorm; - case FW::ImageFormat::RG_88: - return ResourceFormat::RG8Unorm; - case FW::ImageFormat::R8: - return ResourceFormat::R8Unorm; - - case FW::ImageFormat::S3TC_DXT1: - return ResourceFormat::BC1Unorm; - case FW::ImageFormat::S3TC_DXT3: - return ResourceFormat::BC2Unorm; - case FW::ImageFormat::S3TC_DXT5: - return ResourceFormat::BC3Unorm; - case FW::ImageFormat::RGTC_R: - return ResourceFormat::BC4Unorm; - case FW::ImageFormat::RGTC_RG: - return ResourceFormat::BC5Unorm; - - default: - should_not_get_here(); - return ResourceFormat::Unknown; - } - } - - std::string readString(BinaryFileStream& stream) - { - int32_t length; - stream >> length; - std::vector charVec(length + 1); - stream.read(&charVec[0], length); - charVec[length] = 0; - return std::string(charVec.data()); - } - - bool loadBinaryTextureData(BinaryFileStream& stream, const std::string& modelName, TextureData& data) - { - // ImageHeader. - char tag[9]; - stream.read(tag, 8); - tag[8] = '\0'; - if(std::string(tag) != "BinImage") - { - std::string msg = "Error when loading model " + modelName + ".\nBinary image header corrupted."; - logError(msg); - return false; - } - - int32_t version; - stream >> version; - - if(version < 1 || version > 2) - { - std::string msg = "Error when loading model " + modelName + ".\nUnsupported binary image version."; - logError(msg); - return false; - } - - int32_t bpp, numChannels; - stream >> data.width >> data.height >> bpp >> numChannels; - if(data.width < 0 || data.height < 0 || bpp < 0 || numChannels < 0) - { - std::string msg = "Error when loading model " + modelName + ".\nCorrupt binary image version."; - logError(msg); - return false; - } - - int32_t dataSize = -1; - int32_t formatId = FW::ImageFormat::ID_Max; - FW::ImageFormat format; - if(version >= 2) - { - stream >> formatId >> dataSize; - if(formatId < 0 || formatId >= FW::ImageFormat::ID_Generic || dataSize < 0) - { - std::string msg = "Error when loading model " + modelName + ".\nCorrupt binary image data (unsupported image format)."; - logError(msg); - return false; - } - format = FW::ImageFormat(FW::ImageFormat::ID(formatId)); - } - - // Array of ImageChannel. - for(int i = 0; i < numChannels; i++) - { - int32_t ctype, cformat; - FW::ImageFormat::Channel c; - stream >> ctype >> cformat >> c.wordOfs >> c.wordSize >> c.fieldOfs >> c.fieldSize; - if(ctype < 0 || cformat < 0 || cformat >= FW::ImageFormat::ChannelFormat_Max || - c.wordOfs < 0 || (c.wordSize != 1 && c.wordSize != 2 && c.wordSize != 4) || - c.fieldOfs < 0 || c.fieldSize <= 0 || c.fieldOfs + c.fieldSize > c.wordSize * 8 || - (cformat == FW::ImageFormat::ChannelFormat_Float && c.fieldSize != 32)) - { - std::string msg = "Error when loading model " + modelName + ".\nCorrupt binary image data (unsupported floating point format)."; - logError(msg); - return false; - } - - c.Type = (FW::ImageFormat::ChannelType)ctype; - c.format = (FW::ImageFormat::ChannelFormat)cformat; - format.addChannel(c); - } - - if(bpp != format.getBPP()) - { - std::string msg = "Error when loading model " + modelName + ".\nCorrupt binary image data (bits/pixel do not match with format)."; - logError(msg); - return false; - } - - // Format - if(formatId == FW::ImageFormat::ID_Max) - formatId = format.getID(); - data.format = getTextureFormat(FW::ImageFormat::ID(formatId)); - - // Image data. - const int32_t texelCount = data.width * data.height; - if(dataSize == -1) - { - dataSize = bpp * texelCount; - } - size_t storageSize = dataSize; - if(bpp == 3) - storageSize = 4 * texelCount; - - data.data.resize(storageSize); - stream.read(data.data.data(), dataSize); - - // Convert 3-channel 8-bits RGB formats to 4-channel RGBX by adding padding - if(bpp == 3) - { - for(int32_t i=texelCount-1;i>=0;--i) - { - data.data[i * 4 + 0] = data.data[i * 3 + 0]; - data.data[i * 4 + 1] = data.data[i * 3 + 1]; - data.data[i * 4 + 2] = data.data[i * 3 + 2]; - data.data[i * 4 + 3] = 0xff; - } - } - - return true; - } - - bool importTextures(std::vector& textures, uint32_t textureCount, BinaryFileStream& stream, const std::string& modelName) - { - textures.assign(textureCount, TextureData()); - - bool success = true; - for(uint32_t i = 0; i < textureCount; i++) - { - textures[i].name = readString(stream); - if(loadBinaryTextureData(stream, modelName, textures[i]) == false) - { - success = false; - break; - } - } - - // Flush upload heap after every material so we don't accumulate a ton of memory usage when loading a model with a lot of textures - gpDevice->flushAndSync(); - return success; - } - - BinaryModelImporter::BinaryModelImporter(const std::string& fullpath) : mModelName(fullpath), mStream(fullpath.c_str(), BinaryFileStream::Mode::Read) - { - } - - bool BinaryModelImporter::import(Model& model, const std::string& filename, Model::LoadFlags flags) - { - std::string fullpath; - if(findFileInDataDirectories(filename, fullpath) == false) - { - logError(std::string("Can't find model file ") + filename); - return false; - } - - BinaryModelImporter loader(fullpath); - return loader.importModel(model, flags); - } - - static bool checkVersion(const std::string& formatID, uint32_t version, const std::string& modelName) - { - if(std::string(formatID) == "BinScene") - { - if(version < 6 || version > 8) - { - std::string Msg = "Error when loading model " + modelName + ".\nUnsupported binary scene version " + std::to_string(version); - logError(Msg); - return false; - } - } - else if(std::string(formatID) == "BinMesh ") - { - if(version < 1 || version > 5) - { - std::string Msg = "Error when loading model " + modelName + ".\nUnsupported binary scene version " + std::to_string(version); - logError(Msg); - return false; - } - } - else - { - std::string Msg = "Error when loading model " + modelName + ".\nNot a binary scene file!"; - logError(Msg); - return false; - } - return true; - } - - ResourceFormat getFormatFromMapType(bool requestSrgb, ResourceFormat originalFormat, TextureType texType) - { - if(requestSrgb == false) - { - return originalFormat; - } - - switch(texType) - { - case TextureType_Diffuse: - case TextureType_Specular: - case TextureType_Environment: - return srgbToLinearFormat(originalFormat); - default: - return originalFormat; - } - } - - bool BinaryModelImporter::importModel(Model& model, Model::LoadFlags flags) - { - // Format ID and version. - char formatID[9]; - mStream.read(formatID, 8); - formatID[8] = '\0'; - - uint32_t version; - mStream >> version; - - // Check if the version matches - if(checkVersion(formatID, version, mModelName) == false) - { - return false; - } - - int numTextureSlots; - int numAttributesType = AttribType_AORadius + 1; - - switch(version) - { - case 1: numTextureSlots = 0; break; - case 2: numTextureSlots = TextureType_Alpha + 1; break; - case 3: numTextureSlots = TextureType_Displacement + 1; break; - case 4: numTextureSlots = TextureType_Environment + 1; break; - case 5: numTextureSlots = TextureType_Specular + 1; break; - case 6: numTextureSlots = TextureType_Specular + 1; break; - case 7: numTextureSlots = TextureType_Glossiness + 1; break; - case 8: numTextureSlots = TextureType_Glossiness + 1; numAttributesType = AttribType_Max; break; - default: - should_not_get_here(); - return false; - } - - - // File header - int32_t numTextures = 0; - int32_t numMeshes = 0; - int32_t numInstances = 0; - int32_t numAttribs_v5 = 0; - int32_t numVertices_v5 = 0; - int32_t numSubmeshes_v5 = 0; - - if(version >= 6) - { - mStream >> numTextures >> numMeshes >> numInstances; - } - else - { - numMeshes = 1; - numInstances = 1; - mStream >> numAttribs_v5 >> numVertices_v5 >> numSubmeshes_v5; - if(version >= 2) - { - mStream >> numTextures; - } - } - - if(numTextures < 0 || numMeshes < 0 || numInstances < 0) - { - std::string msg = "Error when loading model " + mModelName + ".\nFile is corrupted."; - logError(msg); - return false; - } - - // create objects - bool shouldGenerateTangents = is_set(flags, Model::LoadFlags::DontGenerateTangentSpace) == false; - - std::vector texData; - - if(version >= 6) - { - importTextures(texData, numTextures, mStream, mModelName); - } - - // This file format has a concept of sub-meshes, which Falcor model doesn't have - Falcor creates a new mesh for each sub-mesh - // When creating instances of meshes, it means we need to translate the original mesh index to all it's submeshes Falcor IDs. This is what the next 2 variables are for. - std::vector> meshToSubmeshesID(numMeshes); - - // This importer loads mesh/submesh data before instance data, so the meshes are cached here. - std::vector falcorMeshCache; - - struct TexSignature - { - const uint8_t* pData; - ResourceFormat format; - bool operator<(const TexSignature& other) const - { - if(pData < other.pData) return true; - if(pData == other.pData) return format < other.format; - return false; - } - bool operator==(const TexSignature& other) const { return pData == other.pData || format == other.format; } - }; - std::map textures; - bool loadTexAsSrgb = !is_set(flags, Model::LoadFlags::AssumeLinearSpaceTextures); - - // Load the meshes - for(int meshIdx = 0; meshIdx < numMeshes; meshIdx++) - { - // Mesh header - int32_t numAttribs = 0; - int32_t numVertices = 0; - int32_t numSubmeshes = 0; - - if(version >= 6) - { - mStream >> numAttribs >> numVertices >> numSubmeshes; - } - else - { - numAttribs = numAttribs_v5; - numVertices = numVertices_v5; - numSubmeshes = numSubmeshes_v5; - } - - if(numAttribs < 0 || numVertices < 0 || numSubmeshes < 0) - { - std::string Msg = "Error when loading model " + mModelName + ".\nCorrupted data.!"; - logError(Msg); - return false; - } - - Vao::BufferVec pVBs; - VertexLayout::SharedPtr pLayout = VertexLayout::create(); - - struct BufferData - { - std::vector vec; - bool shouldSkip = false; - uint32_t elementSize = 0; - }; - - std::vector buffers; - pVBs.resize(numAttribs); - buffers.resize(numAttribs); - - const uint32_t kInvalidBufferIndex = (uint32_t)-1; - uint32_t positionBufferIndex = kInvalidBufferIndex; - uint32_t normalBufferIndex = kInvalidBufferIndex; - uint32_t bitangentBufferIndex = kInvalidBufferIndex; - uint32_t texCoordBufferIndex = kInvalidBufferIndex; - - for(int i = 0; i < numAttribs; i++) - { - VertexBufferLayout::SharedPtr pBufferLayout = VertexBufferLayout::create(); - pLayout->addBufferLayout(i, pBufferLayout); - int32_t type, format, length; - mStream >> type >> format >> length; - - if(type < 0 || type >= numAttributesType || format < 0 || format >= AttribFormat::AttribFormat_Max || length < 1 || length > 4) - { - std::string msg = "Error when loading model " + mModelName + ".\nCorrupted data.!"; - logError(msg); - return false; - } - else - { - const std::string falcorName = getSemanticName(AttribType(type)); - ResourceFormat falcorFormat = getFalcorFormat(AttribFormat(format), length); - uint32_t shaderLocation = getShaderLocation(AttribType(type)); - - switch (shaderLocation) - { - case VERTEX_POSITION_LOC: - positionBufferIndex = i; - assert(falcorFormat == ResourceFormat::RGB32Float || falcorFormat == ResourceFormat::RGBA32Float); - break; - case VERTEX_NORMAL_LOC: - normalBufferIndex = i; - assert(falcorFormat == ResourceFormat::RGB32Float); - break; - case VERTEX_BITANGENT_LOC: - bitangentBufferIndex = i; - assert(falcorFormat == ResourceFormat::RGB32Float); - break; - case VERTEX_TEXCOORD_LOC: - texCoordBufferIndex = i; - break; - } - - buffers[i].elementSize = getFormatBytesPerBlock(falcorFormat); - if(shaderLocation != kUnusedShaderElement) - { - pBufferLayout->addElement(falcorName, 0, falcorFormat, 1, shaderLocation); - buffers[i].vec.resize(buffers[i].elementSize * numVertices); - } - else - { - buffers[i].shouldSkip = true; - } - } - } - - - // Check if we need to generate tangents - bool genTangentForMesh = false; - if(shouldGenerateTangents && (bitangentBufferIndex == kInvalidBufferIndex)) - { - if(normalBufferIndex == kInvalidBufferIndex) - { - logWarning("Can't generate tangent space for mesh " + std::to_string(meshIdx) + " when loading model " + mModelName + ".\nMesh doesn't contain normals coordinates\n"); - genTangentForMesh = false; - } - else - { - // Set the offsets - genTangentForMesh = true; - bitangentBufferIndex = (uint32_t)pVBs.size(); - pVBs.resize(bitangentBufferIndex + 1); - buffers.resize(bitangentBufferIndex + 1); - - auto pBitangentLayout = VertexBufferLayout::create(); - pLayout->addBufferLayout(bitangentBufferIndex, pBitangentLayout); - pBitangentLayout->addElement(VERTEX_BITANGENT_NAME, 0, ResourceFormat::RGB32Float, 1, VERTEX_BITANGENT_LOC); - buffers[bitangentBufferIndex].vec.resize(sizeof(glm::vec3) * numVertices); - } - } - - - // Read the data, one vertex at a time - for(int32_t i = 0; i < numVertices; i++) - { - for (int32_t attributes = 0; attributes < numAttribs; ++attributes) - { - if (buffers[attributes].shouldSkip) - { - mStream.skip(buffers[attributes].elementSize); - } - else - { - uint32_t stride = pLayout->getBufferLayout(attributes)->getStride(); - uint8_t* pDest = buffers[attributes].vec.data() + stride * i; - mStream.read(pDest, stride); - } - } - } - - Buffer::BindFlags vbBindFlags = Buffer::BindFlags::Vertex; - if (is_set(flags, Model::LoadFlags::BuffersAsShaderResource)) - { - vbBindFlags |= Buffer::BindFlags::ShaderResource; - } - - for (int32_t i = 0; i < numAttribs; ++i) - { - if(buffers[i].shouldSkip == false) - { - pVBs[i] = Buffer::create(buffers[i].vec.size(), vbBindFlags, Buffer::CpuAccess::None, buffers[i].vec.data()); - } - } - - if(version <= 5) - { - importTextures(texData, numTextures, mStream, mModelName); - textures.clear(); - } - - // Array of Submesh. - // Falcor doesn't have a concept of submeshes, just create a new mesh for each submesh - for(int submesh = 0; submesh < numSubmeshes; submesh++) - { - // create the material - Material::SharedPtr pMaterial = Material::create(""); - - glm::vec3 ambient; - glm::vec4 diffuse; - glm::vec3 specular; - float glossiness; - - mStream >> ambient >> diffuse >> specular >> glossiness; - diffuse.w = 1 - diffuse.w; - pMaterial->setBaseColor(diffuse); - pMaterial->setSpecularParams(vec4(specular, glossiness)); - - if(version >= 3) - { - float displacementCoeff; - float displacementBias; - mStream >> displacementCoeff >> displacementBias; - pMaterial->setHeightScaleOffset(displacementCoeff, displacementBias); - } - - for(int i = 0; i < numTextureSlots; i++) - { - int32_t texID; - mStream >> texID; - if(texID < -1 || texID >= numTextures) - { - std::string msg = "Error when loading model " + mModelName + ".\nCorrupt binary mesh data!"; - logError(msg); - return false; - } - else if(texID != -1) - { - // Load the texture - TexSignature texSig; - texSig.format = getFormatFromMapType(loadTexAsSrgb, texData[texID].format, TextureType(i)); - texSig.pData = texData[texID].data.data(); - // Check if we already created a matching texture - auto existingTex = textures.find(texSig); - if(existingTex != textures.end()) - { - setTexture(pMaterial.get(), existingTex->second, TextureType(i), mModelName); - } - else - { - auto pTexture = Texture::create2D(texData[texID].width, texData[texID].height, texSig.format, 1, Texture::kMaxPossible, texSig.pData); - pTexture->setSourceFilename(texData[texID].name); - textures[texSig] = pTexture; - setTexture(pMaterial.get(), pTexture, TextureType(i), mModelName); - } - } - } - - // Create material and check if it already exists - pMaterial = checkForExistingMaterial(pMaterial); - - int32_t numTriangles; - mStream >> numTriangles; - if(numTriangles < 0) - { - std::string Msg = "Error when loading model " + mModelName + ".\nMesh has negative number of triangles!"; - logError(Msg); - return false; - } - - // create the index buffer - uint32_t numIndices = numTriangles * 3; - std::vector indices(numIndices); - uint32_t ibSize = 3 * numTriangles * sizeof(uint32_t); - mStream.read(&indices[0], ibSize); - - - Buffer::BindFlags ibBindFlags = Buffer::BindFlags::Index; - if (is_set(flags, Model::LoadFlags::BuffersAsShaderResource)) - { - ibBindFlags |= Buffer::BindFlags::ShaderResource; - } - auto pIB = Buffer::create(ibSize, ibBindFlags, Buffer::CpuAccess::None, indices.data()); - - // Generate tangent space data if needed - if(genTangentForMesh) - { - uint32_t texCrdCount = 0; - glm::vec2* texCrd = nullptr; - if(texCoordBufferIndex != kInvalidBufferIndex) - { - texCrdCount = pLayout->getBufferLayout(texCoordBufferIndex)->getStride() / sizeof(glm::vec2); - texCrd = (glm::vec2*)buffers[texCoordBufferIndex].vec.data(); - } - - ResourceFormat posFormat = pLayout->getBufferLayout(positionBufferIndex)->getElementFormat(0); - - if (posFormat == ResourceFormat::RGB32Float) - { - generateSubmeshTangentData(indices, numVertices, (glm::vec3*)buffers[positionBufferIndex].vec.data(), (glm::vec3*)buffers[normalBufferIndex].vec.data(), texCrd, texCrdCount, (glm::vec3*)buffers[bitangentBufferIndex].vec.data()); - } - else if (posFormat == ResourceFormat::RGBA32Float) - { - generateSubmeshTangentData(indices, numVertices, (glm::vec4*)buffers[positionBufferIndex].vec.data(), (glm::vec3*)buffers[normalBufferIndex].vec.data(), texCrd, texCrdCount, (glm::vec3*)buffers[bitangentBufferIndex].vec.data()); - } - - pVBs[bitangentBufferIndex] = Buffer::create(buffers[bitangentBufferIndex].vec.size(), Buffer::BindFlags::Vertex, Buffer::CpuAccess::None, buffers[bitangentBufferIndex].vec.data()); - } - - - // Calculate the bounding-box - glm::vec3 max, min; - for(uint32_t i = 0; i < numIndices; i++) - { - uint32_t vertexID = indices[i]; - uint8_t* pVertex = (pLayout->getBufferLayout(positionBufferIndex)->getStride() * vertexID) + buffers[positionBufferIndex].vec.data(); - - float* pPosition = (float*)pVertex; - - glm::vec3 xyz(pPosition[0], pPosition[1], pPosition[2]); - min = glm::min(min, xyz); - max = glm::max(max, xyz); - } - - BoundingBox box = BoundingBox::fromMinMax(min, max); - - // create the mesh - auto pMesh = Mesh::create(pVBs, numVertices, pIB, numIndices, pLayout, Vao::Topology::TriangleList, pMaterial, box, false); - - if (version >= 6) - { - falcorMeshCache.push_back(pMesh); - meshToSubmeshesID[meshIdx].push_back((uint32_t)(falcorMeshCache.size() - 1)); - } - else - { - model.addMeshInstance(pMesh, glm::mat4()); - } - } - } - - if(version >= 6) - { - for(int32_t instanceID = 0; instanceID < numInstances; instanceID++) - { - int32_t meshIdx = 0; - int32_t enabled = 1; - glm::mat4 transformation; - - mStream >> meshIdx >> enabled >> transformation; - //m_Stream >> inst.name >> inst.metadata; - readString(mStream); // Name - readString(mStream); // Meta-data - - if(enabled) - { - for(uint32_t i : meshToSubmeshesID[meshIdx]) - { - model.addMeshInstance(falcorMeshCache[i], transformation); - } - } - } - } - - return true; - } -} diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryModelSpec.h b/Framework/Source/Graphics/Model/Loaders/BinaryModelSpec.h deleted file mode 100644 index 7e78b83c4..000000000 --- a/Framework/Source/Graphics/Model/Loaders/BinaryModelSpec.h +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once - -//------------------------------------------------------------------------ -/* - -Binary scene file format v8 ---------------------------- - -- The basic units of data are 32-bit little-endian ints and floats. -- In addition to the latest version, the below specification also describes previous versions of the file format. -- Each individual field is marked with the version number where it was introduced. -- Legacy structs are postfixed with the highest version number for which they are still valid. -- Each line describes: () - -File -0 2 string8 v6 formatID ("BinScene") -2 1 int v6 formatVersion (6) -3 1 int v6 numTextures -4 1 int v6 numMeshes -5 1 int v6 numInstances -6 n*? array v6 Texture (numTextures) -? n*? array v6 Mesh (numMeshes) -? n*? array v6 Instance (numInstances) -? - -File_v5 -0 2 string8 v1 formatID ("BinMesh ") -2 1 int v1 formatVersion (1 .. 5) -3 1 int v1 numAttribs -4 1 int v1 numVertices -5 1 int v2 numTextures -6 1 int v1 numSubmeshes -7 n*3 array v1 AttribSpec (numAttribs) -? n*? array v1 Vertex (numVertices) -? n*? array v2 Texture (numTextures) -? n*? array v1 Submesh (numSubmeshes) -? - -Texture -0 1 int v2 idLength -1 ? string v2 idString -? ? struct v2 BinaryImage (see ImageBinaryIO.hpp) -? - -Mesh -0 1 int v6 numAttribs -1 1 int v6 numVertices -2 1 int v6 numSubmeshes -3 n*3 array v6 AttribSpec (numAttribs) -? n*? array v6 Vertex (numVertices) -? n*? array v6 Submesh (numSubmeshes) -? - -AttribSpec -0 1 int v1 Type (see MeshBase::AttribType) -1 1 int v1 format (see MeshBase::AttribFormat) -2 1 int v1 length -3 - -Vertex -0 ? bytes v1 vertex data (dictated by the AttribSpecs) -? - -Submesh -0 3 float v1 ambient (ignored) -3 4 float v1 diffuse -7 3 float v1 specular -10 1 float v1 glossiness -11 1 float v3 displacementCoef -12 1 float v3 displacementBias -13 1 int v2 diffuseTexture (-1 if none) -14 1 int v2 alphaTexture (-1 if none) -15 1 int v3 displacementTexture (-1 if none) -16 1 int v4 normalTexture (-1 if none) -17 1 int v4 environmentTexture (-1 if none) -18 1 int v5 specularTexture (-1 if none) -19 1 int v1 numTriangles -20 n*3 int v1 indices (numTriangles * 3) -? - -Instance -0 1 int v6 meshIdx (-1 if none) -1 1 bool v6 enabled -2 16 float v6 meshToWorld (column-major 4x4 matrix) -18 1 int v6 nameLength -19 ? string v6 nameString -? 1 int v6 metadataLength -? ? string v6 metadataString -? - -*/ -//------------------------------------------------------------------------ - -enum AttribType // allows arbitrary values -{ - AttribType_Position = 0, // (x, y, z) or (x, y, z, w) - AttribType_Normal, // (x, y, z) - AttribType_Color, // (r, g, b) or (r, g, b, a) - AttribType_TexCoord, // (u, v) or (u, v, w) - - AttribType_AORadius, // (min, max) - - - AttribType_Tangent, // (x, y, z) - AttribType_Bitangent, // (x, y, z) - - AttribType_Max -}; - -enum AttribFormat -{ - AttribFormat_U8 = 0, - AttribFormat_S32, - AttribFormat_F32, - - AttribFormat_Max -}; - -enum TextureType -{ - TextureType_Diffuse = 0, // Diffuse color map. - TextureType_Alpha, // Alpha map (green = opacity). - TextureType_Displacement, // Displacement map (green = height). - TextureType_Normal, // Tangent-space normal map. - TextureType_Environment, // Environment map (spherical coordinates). - TextureType_Specular, // Specular color map. - TextureType_Glossiness, // Glossiness map. - TextureType_Max -}; diff --git a/Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.cpp b/Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.cpp deleted file mode 100644 index 6abab8b9f..000000000 --- a/Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "SimpleModelImporter.h" -#include "../Model.h" -#include "../Mesh.h" -#include "Utils/Platform/OS.h" -#include "API/VertexLayout.h" -#include "Data/VertexAttrib.h" -#include "API/Buffer.h" -#include "API/Formats.h" -#include "API/Texture.h" - -namespace Falcor -{ - - Model::SharedPtr SimpleModelImporter::create( VertexFormat vertLayout, uint32_t vboSz, const void *vboData, - uint32_t idxBufSz, const uint32_t *idxBufData, Texture::SharedPtr diffuseTexture, - Vao::Topology geomTopology ) - { - // Since SimpleModelImporter is all static, create an instance here to help track materials - SimpleModelImporter modelImporter; - - // Create our model container - Model::SharedPtr pModel = Model::create(); - - // Need to create our vertex layout - VertexBufferLayout::SharedPtr pVertexLayout = VertexBufferLayout::create(); - uint32_t vertexStride = 0; - uint32_t positionOffset = 0; - for ( int i = 0; i < vertLayout.attribs.size(); i++ ) - { - // Convert the vertex attrib structure into what we need internally in this loop - uint32_t type = uint32_t( vertLayout.attribs[i].attribType ); - AttribFormat format = vertLayout.attribs[i].attribFormat; - int32_t length = vertLayout.attribs[i].numElements; - if ( length <= 0 || length > 4 ) - continue; - - // If this is a "position" attribute, remember the offset, since we'll use this later to - // compute a bounding box for the entire mesh - if ( vertLayout.attribs[i].attribType == AttribType::Position ) - positionOffset = vertexStride; - - // Do some conversions to the format we need data in to set a Falcor vertex attribute entry - ResourceFormat falcorFormat = getResourceFormat( format, length ); - const std::string falcorName = getSemanticName( vertLayout.attribs[i].attribType ); - - // Add this vertex attribute to our format - pVertexLayout->addElement( falcorName, vertexStride, falcorFormat, 1, type ); - - // Add this attribute's size to our per-vertex size. - uint32_t size = getFormatByteSize( AttribFormat( format ) ) * length; - vertexStride += size; - } - - // Create vertex buffer and add to the model - VertexLayout::SharedPtr pLayout = VertexLayout::create(); - pLayout->addBufferLayout(0, pVertexLayout); - Buffer::SharedPtr pBuffer = Buffer::create( vboSz, Buffer::BindFlags::Vertex, Buffer::CpuAccess::None, vboData ); - - // Create index buffer and add to the model - Buffer::SharedPtr pIB = Buffer::create( idxBufSz, Buffer::BindFlags::Index, Buffer::CpuAccess::None, idxBufData ); - - // Compute more explicit / traditional counts needed internally - uint32_t numVertices = vboSz / vertexStride; - uint32_t numIndicies = idxBufSz / (sizeof( uint32_t )); - - // Create a really simple, dumb material for this mesh - Material::SharedPtr pMaterial = Material::create(""); - if ( diffuseTexture ) - { - pMaterial->setBaseColorTexture(diffuseTexture); - } - pMaterial->setBaseColor(vec4(1)); - pMaterial = modelImporter.checkForExistingMaterial(pMaterial); - - // Calculate a bounding-box for this model - glm::vec3 posMax, posMin; - for ( uint32_t i = 0; i < numIndicies; i++ ) - { - // Find a pointer to the floats containing our vertex position - uint32_t vertexID = idxBufData[i]; - uint8_t* pVertex = ((uint8_t *) vboData) + ( vertexStride * vertexID ); - float* pPosition = (float*) (pVertex + positionOffset); - - glm::vec3 xyz( pPosition[0], pPosition[1], pPosition[2] ); - posMin = glm::min( posMin, xyz ); - posMax = glm::max( posMax, xyz ); - } - BoundingBox box = BoundingBox::fromMinMax( posMin, posMax ); - - // create a mesh containing this index & vertex data. - Mesh::SharedPtr pMesh = Mesh::create({ pBuffer }, numVertices, pIB, numIndicies, pLayout, geomTopology, pMaterial, box, false); - pModel->addMeshInstance(pMesh, glm::mat4()); // Add this mesh to the model - - // Do internal computations on model properties - pModel->calculateModelProperties(); - - // Done - return pModel; - } - - ResourceFormat SimpleModelImporter::getResourceFormat( AttribFormat format, uint32_t components ) - { - ResourceFormat byteFormats[4] = { ResourceFormat::R8Unorm, ResourceFormat::RG8Unorm, ResourceFormat::RGBA8Unorm, ResourceFormat::RGBA8Unorm }; - ResourceFormat intFormats[4] = { ResourceFormat::R32Int, ResourceFormat::RG32Int, ResourceFormat::RGB32Int, ResourceFormat::RGBA32Int }; - ResourceFormat floatFormats[4] = { ResourceFormat::R32Float, ResourceFormat::RG32Float, ResourceFormat::RGB32Float, ResourceFormat::RGBA32Float }; - - if ( format == AttribFormat::AttribFormat_U8 ) - return byteFormats[clamp( int( components ) - 1, 0, 3 )]; - else if ( format == AttribFormat::AttribFormat_S32 ) - return intFormats[clamp( int( components ) - 1, 0, 3 )]; - else if ( format == AttribFormat::AttribFormat_F32 ) - return floatFormats[clamp( int( components ) - 1, 0, 3 )]; - - should_not_get_here(); - return ResourceFormat::Unknown; - } - - int32_t SimpleModelImporter::getFormatByteSize( AttribFormat format ) - { - switch ( format ) - { - case AttribFormat_U8: - return 1; - case AttribFormat_S32: - case AttribFormat_F32: - return 4; - default: - should_not_get_here(); - return 0; - } - } - - const std::string SimpleModelImporter::getSemanticName( AttribType type ) - { - switch ( type ) - { - case AttribType::Position: - return VERTEX_POSITION_NAME; - case AttribType::Normal: - return VERTEX_NORMAL_NAME; - case AttribType::Color: - return VERTEX_DIFFUSE_COLOR_NAME; - case AttribType::TexCoord: - return VERTEX_TEXCOORD_NAME; - case AttribType::Bitangent: - return VERTEX_BITANGENT_NAME; - case AttribType::BoneWeight: - return VERTEX_BONE_WEIGHT_NAME; - case AttribType::BoneID: - return VERTEX_BONE_ID_NAME; - case AttribType::User0: - return "USER0"; - case AttribType::User1: - return "USER1"; - case AttribType::User2: - return "USER2"; - case AttribType::User3: - return "USER3"; - default: - should_not_get_here(); - return ""; - } - } - -} // end namespace Falcor \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.h b/Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.h deleted file mode 100644 index fe1c9965b..000000000 --- a/Framework/Source/Graphics/Model/Loaders/SimpleModelImporter.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -//#include "Utils/BinaryFileStream.h" -#include "BinaryModelSpec.h" -#include "../Model.h" -#include "glm/vec3.hpp" -#include "Data/VertexAttrib.h" -#include "Graphics/Model/Loaders/ModelImporter.h" - -namespace Falcor -{ - class Texture; - - // Prior Falcor classes load models from disk via some format. Chris wanted some way to create - // models on the fly from memory resources. This class is a start at doing this. - // Note 1: this is pretty simplistic and may not correctly integrate with fancy materials, etc. - // Note 2: this may not correctly setup model to interact with DirectX shaders. In particular, - // vertex attribute names are required, and these may not all be set correctly! - deprecate("3.3", "") - class SimpleModelImporter : public ModelImporter - { - public: - - // Where can we attach vertex attributes in our shader? - enum class AttribType - { - Position = VERTEX_POSITION_LOC, - Normal = VERTEX_NORMAL_LOC, - Bitangent = VERTEX_BITANGENT_LOC, - BoneWeight = VERTEX_BONE_WEIGHT_LOC, - BoneID = VERTEX_BONE_ID_LOC, - Color = VERTEX_DIFFUSE_COLOR_LOC, - TexCoord = VERTEX_TEXCOORD_LOC, - User0 = VERTEX_USER0_LOC, - User1 = VERTEX_USER0_LOC + 1, - User2 = VERTEX_USER0_LOC + 2, - User3 = VERTEX_USER0_LOC + 3, - }; - - // What format are the attributes do we have for this model? - struct VertexAttrib - { - SimpleModelImporter::AttribType attribType; - uint32_t numElements; - AttribFormat attribFormat; - }; - - // What format are our vertices stored in memory? - struct VertexFormat - { - std::vector attribs; - }; - - // Create a model made up of a number of triangles, layed out (in the index buffer) as GL_TRIANGLES - static Model::SharedPtr create( VertexFormat vertLayout, uint32_t vboSz, const void *vboData, - uint32_t idxBufSz, const uint32_t *idxData, - Texture::SharedPtr diffuseTexture = nullptr, - Vao::Topology geomTopology = Vao::Topology::TriangleList ); - - private: - static ResourceFormat getResourceFormat( AttribFormat format, uint32_t components ); - static int32_t getFormatByteSize( AttribFormat format ); - static const std::string getSemanticName( AttribType type ); - }; -} diff --git a/Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.cpp b/Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.cpp deleted file mode 100644 index fdd93573c..000000000 --- a/Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.cpp +++ /dev/null @@ -1,1140 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Image.hpp" -//#include "gpu/CudaModule.hpp" - -using namespace FW; - -//------------------------------------------------------------------------ - -#define C8(TYPE, OFS) { ChannelType_ ## TYPE, ChannelFormat_Clamp, OFS, 1, 0, 8 } -#define C16(TYPE, OFS, SIZE) { ChannelType_ ## TYPE, ChannelFormat_Clamp, 0, 2, OFS, SIZE } -#define C32(TYPE, OFS) { ChannelType_ ## TYPE, ChannelFormat_Clamp, 0, 4, OFS, 8 } -#define CF32(TYPE, OFS) { ChannelType_ ## TYPE, ChannelFormat_Float, OFS, 4, 0, 32 } - -const ImageFormat::StaticFormat ImageFormat::s_staticFormats[] = -{ - /* R8_G8_B8 */ { 3, 3, { C8(R,0), C8(G,1), C8(B,2) }, /*GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, false */}, - /* R8_G8_B8_A8 */ { 4, 4, { C8(R,0), C8(G,1), C8(B,2), C8(A,3) }, /*GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false */}, - /* A8 */ { 1, 1, { C8(A,0) }, /*GL_ALPHA8, GL_ALPHA, GL_UNSIGNED_BYTE, false */}, - /* XBGR_8888 */ { 4, 3, { C32(R,0), C32(G,8), C32(B,16) }, /*GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, true */}, - /* ABGR_8888 */ { 4, 4, { C32(R,0), C32(G,8), C32(B,16), C32(A,24) }, /*GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, true */}, - - /* RGB_565 */ { 2, 3, { C16(R,11,5), C16(G,5,6), C16(B,0,5) }, /*GL_RGB5, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false */}, - /* RGBA_5551 */ { 2, 4, { C16(R,11,5), C16(G,6,5), C16(B,1,5), C16(A,0,1) }, /*GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, false */}, - - /* RGB_Vec3f */ { 12, 3, { CF32(R,0), CF32(G,4), CF32(B,8) }, /*GL_RGB32F, GL_RGB, GL_FLOAT, false */}, - /* RGBA_Vec4f */ { 16, 4, { CF32(R,0), CF32(G,4), CF32(B,8), CF32(A,12) }, /*GL_RGBA32F, GL_RGBA, GL_FLOAT, false */}, - /* A_F32 */ { 4, 1, { CF32(A,0) }, /*GL_ALPHA32F_ARB, GL_ALPHA, GL_FLOAT, false */}, - - /*BGRA_8888*/ { 4, 4, { C8(R,0), C8(G,1), C8(B,2), C8(A, 3) }, /*GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, false */}, - /*BGR_888*/ { 3, 3, { C8(R,0), C8(G,1), C8(B,2) }, /*GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE, false */}, - /*RG_88*/ { 2, 2, { C8(R,0), C8(G,1), }, /*GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false */}, - /*R8*/ { 1, 1, { C8(R,0)}, /*GL_R, GL_RED, GL_UNSIGNED_BYTE, false */}, - - /*S3TC_DXT1*/ { 0, 0, {}, /*GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_NONE, false*/}, - /*S3TC_DXT3*/ { 0, 0, {}, /*GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_NONE, false*/}, - /*S3TC_DXT5*/ { 0, 0, {}, /*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_NONE, false*/}, - /*RGTC_R*/ { 0, 0, {}, /*GL_COMPRESSED_RED_RGTC1, GL_RED, GL_NONE, false*/}, - /*RGTC_RG*/ { 0, 0, {}, /*GL_COMPRESSED_RG_RGTC2, GL_RG, GL_NONE, false*/}, -}; - -#undef C8 -#undef C16 -#undef C32 -#undef CF32 - -//------------------------------------------------------------------------ - -#define RGB_565_TO_ABGR_8888(V) ((V >> 8) & 0x000000F8) | (V >> 13) | ((V << 5) & 0x0000FC00) | ((V >> 1) & 0x00000300) | ((V << 19) & 0x00F80000) | ((V >> 14) & 0x00070000) | 0xFF000000 -#define ABGR_8888_TO_RGB_565(V) (U16)(((V << 8) & 0xF800) | ((V >> 5) & 0x07E0) | ((V >> 19) & 0x001F)) - -#define RGBA_5551_TO_ABGR_8888(V) ((V >> 8) & 0x000000F8) | (V >> 13) | ((V << 5) & 0x0000F800) | (V & 0x00000700) | ((V << 18) & 0x00F80000) | ((V >> 13) & 0x00070000) | ((S32)(V << 31) >> 7) -#define ABGR_8888_TO_RGBA_5551(V) (U16)(((V << 8) & 0xF800) | ((V >> 5) & 0x07C0) | ((V >> 18) & 0x003E) | (V >> 31)) - -//------------------------------------------------------------------------ - -ImageFormat::ID ImageFormat::getID(void) const -{ - if (m_id != ID_Max) - return m_id; - - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - for (int i = 0; i < (int)ID_Generic; i++) - { - const StaticFormat& f = s_staticFormats[i]; - if (m_genericBPP == f.bpp && - m_genericChannels.size() == f.numChannels && - memcmp(m_genericChannels.data(), f.channels, m_genericChannels.size()*sizeof(m_genericChannels[0])) == 0) - { - m_id = (ID)i; - return m_id; - } - } - - m_id = ID_Generic; - return m_id; -} - -//------------------------------------------------------------------------ - -const ImageFormat::StaticFormat* ImageFormat::getStaticFormat(void) const -{ - ID id = getID(); - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (id < ID_Generic) ? &s_staticFormats[id] : NULL; -} - -//------------------------------------------------------------------------ - -int ImageFormat::getBPP(void) const -{ - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (m_id < ID_Generic) ? s_staticFormats[m_id].bpp : m_genericBPP; -} - -//------------------------------------------------------------------------ - -int ImageFormat::getNumChannels(void) const -{ - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (m_id < ID_Generic) ? s_staticFormats[m_id].numChannels : (int)m_genericChannels.size(); -} - -//------------------------------------------------------------------------ - -const ImageFormat::Channel& ImageFormat::getChannel(int idx) const -{ - FW_ASSERT(idx >= 0 && idx < getNumChannels()); - FW_ASSERT(FW_ARRAY_SIZE(s_staticFormats) == ID_Generic); - return (m_id < ID_Generic) ? s_staticFormats[m_id].channels[idx] : m_genericChannels[idx]; -} - -//------------------------------------------------------------------------ - -int ImageFormat::findChannel(ChannelType Type) const -{ - int num = getNumChannels(); - for (int i = 0; i < num; i++) - if (getChannel(i).Type == Type) - return i; - return -1; -} - -//------------------------------------------------------------------------ - -void ImageFormat::set(const ImageFormat& other) -{ - m_id = other.m_id; - if (m_id >= ID_Generic) - { - m_genericBPP = other.m_genericBPP; - m_genericChannels = other.m_genericChannels; - } -} - -//------------------------------------------------------------------------ - -void ImageFormat::clear(void) -{ - m_id = ID_Generic; - m_genericBPP = 0; - m_genericChannels.clear(); -} - -//------------------------------------------------------------------------ - -void ImageFormat::addChannel(const Channel& channel) -{ - if (m_id < ID_Generic) - { - const StaticFormat& f = s_staticFormats[m_id]; - m_genericBPP = f.bpp; - m_genericChannels.resize(f.numChannels); - for(int32_t i = 0 ; i < f.numChannels ; i++) - { - m_genericChannels[i] = f.channels[i]; - } - } - - m_id = ID_Max; - m_genericBPP = max(m_genericBPP, channel.wordOfs + channel.wordSize); - m_genericChannels.push_back(channel); -} - -//------------------------------------------------------------------------ - -#if 0 -ImageFormat::ID ImageFormat::getGLFormat(void) const -{ - const ImageFormat::StaticFormat* sf = getStaticFormat(); - - // Requires little endian machine => check. - - if (sf && sf->glLittleEndian) - { - U32 tmp = 0x12345678; - if (*(U8*)&tmp != 0x78) - sf = NULL; - } - - // Maps directly to a GL format => done. - - if (sf && sf->glInternalFormat != GL_NONE) - return getID(); - - // Otherwise => select the closest match. - - U32 channels = 0; - bool isFloat = false; - for (int i = 0; i < getNumChannels(); i++) - { - const ImageFormat::Channel& c = getChannel(i); - if (c.Type > ImageFormat::ChannelType_A) - continue; - - channels |= 1 < c.Type; - if (c.format == ImageFormat::ChannelFormat_Float) - isFloat = true; - } - - if ((channels & 7) == 0) - return (isFloat) ? ImageFormat::A_F32 : ImageFormat::A8; - if ((channels & 8) == 0) - return (isFloat) ? ImageFormat::RGB_Vec3f : ImageFormat::R8_G8_B8; - return (isFloat) ? ImageFormat::RGBA_Vec4f : ImageFormat::R8_G8_B8_A8; -} - -//------------------------------------------------------------------------ - -bool ImageFormat::operator==(const ImageFormat& other) const -{ - if (m_id < ID_Generic || other.m_id < ID_Generic) - return (getID() == other.getID()); - - return ( - m_genericBPP == other.m_genericBPP && - m_genericChannels.size() == other.m_genericChannels.size() && - memcmp(m_genericChannels.data(), other.m_genericChannels.data(), m_genericChannels.size() * sizeof(m_genericChannels[0]) == 0)); -} - -//------------------------------------------------------------------------ -Image::Image(const Vec2i& size, const ImageFormat& format, void* ptr, S64 stride) -{ - init(size, format); - FW_ASSERT(size.min() == 0 || ptr); - - S64 lo = 0; - S64 hi = 0; - if (size.min() != 0) - { - lo = min(stride * (size.y - 1), (S64)0); - hi = max(stride * (size.y - 1), (S64)0) + size.x * format.getBPP(); - } - - m_stride = stride; - m_buffer = new Buffer((U8*)ptr + lo, hi - lo); - m_ownBuffer = true; - m_offset = -lo; -} - -//------------------------------------------------------------------------ - -Image::Image(const Vec2i& size, const ImageFormat& format, Buffer& buffer, S64 ofs, S64 stride) -{ - init(size, format); - FW_ASSERT(size.min() == 0 || ofs + min(stride * (size.y - 1), (S64)0) >= 0); - FW_ASSERT(size.min() == 0 || ofs + max(stride * (size.y - 1), (S64)0) + size.x * format.getBPP() <= buffer.getSize()); - - m_stride = stride; - m_buffer = &buffer; - m_ownBuffer = false; - m_offset = ofs; -} - -//------------------------------------------------------------------------ - -Image::~Image(void) -{ - if (m_ownBuffer) - delete m_buffer; -} - -//------------------------------------------------------------------------ - -U32 Image::getABGR(const Vec2i& pos) const -{ - FW_ASSERT(contains(pos, 1)); - const U8* p = getPtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::R8_G8_B8: return p[0] | (p[1] << 8) | (p[2] << 16) | 0xFF000000; - case ImageFormat::R8_G8_B8_A8: return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); - case ImageFormat::A8: return *p << 24; - case ImageFormat::XBGR_8888: return *(const U32*)p | 0xFF000000; - case ImageFormat::ABGR_8888: return *(const U32*)p; - - case ImageFormat::RGB_565: { U16 v = *(const U16*)p; return RGB_565_TO_ABGR_8888(v); } - case ImageFormat::RGBA_5551: { U16 v = *(const U16*)p; return RGBA_5551_TO_ABGR_8888(v); } - - case ImageFormat::RGB_Vec3f: return Vec4f(*(const Vec3f*)p, 1.0f).toABGR(); - case ImageFormat::RGBA_Vec4f: return ((const Vec4f*)p)->toABGR(); - case ImageFormat::A_F32: return clamp((int)(*(const F32*)p * 255.0f + 0.5f), 0x00, 0xFF) << 24; - - default: - { - getChannels(pos); - bool hasAlpha = false; - U32 value = 0; - - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - U32 v = clamp((int)(m_channelTmp[i] * 255.0f + 0.5f), 0x00, 0xFF); - switch (m_format.getChannel(i).Type) - { - case ImageFormat::ChannelType_R: value |= v; break; - case ImageFormat::ChannelType_G: value |= v << 8; break; - case ImageFormat::ChannelType_B: value |= v << 16; break; - case ImageFormat::ChannelType_A: value |= v << 24; hasAlpha = true; break; - } - } - - if (!hasAlpha) - value |= 0xFF000000; - return value; - } - } -} - -//------------------------------------------------------------------------ - -void Image::setABGR(const Vec2i& pos, U32 value) -{ - FW_ASSERT(contains(pos, 1)); - U8* p = getMutablePtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::R8_G8_B8: p[0] = (U8)value; p[1] = (U8)(value >> 8); p[2] = (U8)(value >> 16); break; - case ImageFormat::R8_G8_B8_A8: p[0] = (U8)value; p[1] = (U8)(value >> 8); p[2] = (U8)(value >> 16); p[3] = (U8)(value >> 24); break; - case ImageFormat::A8: *p = (U8)(value >> 24); break; - case ImageFormat::XBGR_8888: *(U32*)p = value; break; - case ImageFormat::ABGR_8888: *(U32*)p = value; break; - - case ImageFormat::RGB_565: *(U16*)p = ABGR_8888_TO_RGB_565(value); break; - case ImageFormat::RGBA_5551: *(U16*)p = ABGR_8888_TO_RGBA_5551(value); break; - - case ImageFormat::RGB_Vec3f: *(Vec3f*)p = Vec4f::fromABGR(value).getXYZ(); break; - case ImageFormat::RGBA_Vec4f: *(Vec4f*)p = Vec4f::fromABGR(value); break; - case ImageFormat::A_F32: *(F32*)p = (F32)(value >> 24) / 255.0f; break; - - default: - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - F32& channel = m_channelTmp[i]; - switch (m_format.getChannel(i).Type) - { - case ImageFormat::ChannelType_R: channel = (F32)(value & 0xFF) / 255.0f; break; - case ImageFormat::ChannelType_G: channel = (F32)((value >> 8) & 0xFF) / 255.0f; break; - case ImageFormat::ChannelType_B: channel = (F32)((value >> 16) & 0xFF) / 255.0f; break; - case ImageFormat::ChannelType_A: channel = (F32)(value >> 24) / 255.0f; break; - default: channel = 0.0f; break; - } - } - setChannels(pos, m_channelTmp.getPtr()); - break; - } -} - -//------------------------------------------------------------------------ - -Vec4f Image::getVec4f(const Vec2i& pos) const -{ - FW_ASSERT(contains(pos, 1)); - const U8* p = getPtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::A8: return Vec4f(0.0f, 0.0f, 0.0f, (F32)(*p / 255.0f)); - case ImageFormat::XBGR_8888: return Vec4f::fromABGR(*(const U32*)p | 0xFF000000); - case ImageFormat::ABGR_8888: return Vec4f::fromABGR(*(const U32*)p); - case ImageFormat::RGB_Vec3f: return Vec4f(*(const Vec3f*)p, 1.0f); - case ImageFormat::RGBA_Vec4f: return *(const Vec4f*)p; - case ImageFormat::A_F32: return Vec4f(0.0f, 0.0f, 0.0f, *(const F32*)p); - - case ImageFormat::R8_G8_B8: - case ImageFormat::R8_G8_B8_A8: - case ImageFormat::RGB_565: - case ImageFormat::RGBA_5551: - return Vec4f::fromABGR(getABGR(pos)); - - default: - { - getChannels(pos); - Vec4f value(0.0f, 0.0f, 0.0f, 1.0f); - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - ImageFormat::ChannelType t = m_format.getChannel(i).Type; - if (t <= ImageFormat::ChannelType_A) - value[t] = m_channelTmp[i]; - } - return value; - } - } -} - -//------------------------------------------------------------------------ - -void Image::setVec4f(const Vec2i& pos, const Vec4f& value) -{ - FW_ASSERT(contains(pos, 1)); - U8* p = getMutablePtr(pos); - - switch (m_format.getID()) - { - case ImageFormat::A8: *p = (U8)clamp((int)(value.w * 255.0f + 0.5f), 0x00, 0xFF); break; - case ImageFormat::XBGR_8888: *(U32*)p = value.toABGR(); break; - case ImageFormat::ABGR_8888: *(U32*)p = value.toABGR(); break; - case ImageFormat::RGB_Vec3f: *(Vec3f*)p = value.getXYZ(); break; - case ImageFormat::RGBA_Vec4f: *(Vec4f*)p = value; break; - case ImageFormat::A_F32: *(F32*)p = value.w; break; - - case ImageFormat::R8_G8_B8: - case ImageFormat::R8_G8_B8_A8: - case ImageFormat::RGB_565: - case ImageFormat::RGBA_5551: - setABGR(pos, value.toABGR()); - break; - - default: - for (int i = 0; i < m_channelTmp.getSize(); i++) - { - ImageFormat::ChannelType t = m_format.getChannel(i).Type; - m_channelTmp[i] = (t <= ImageFormat::ChannelType_A) ? value[t] : 0.0f; - } - setChannels(pos, m_channelTmp.getPtr()); - break; - } -} - -//------------------------------------------------------------------------ - -void Image::flipX(void) -{ - int bpp = getBPP(); - for (int y = 0; y < m_size.y; y++) - { - U8* ptrA = getMutablePtr(Vec2i(0, y)); - U8* ptrB = getMutablePtr(Vec2i(m_size.x - 1, y)); - for (int x = (m_size.x >> 1); x > 0; x--) - { - for (int i = 0; i < bpp; i++) - swap(ptrA[i], ptrB[i]); - ptrA += bpp; - ptrB -= bpp; - } - } -} - -//------------------------------------------------------------------------ - -void Image::flipY(void) -{ - int scanBytes = m_size.x * getBPP(); - Array tmp(NULL, scanBytes); - for (int y = (m_size.y >> 1) - 1; y >= 0; y--) - { - U8* ptrA = getMutablePtr(Vec2i(0, y)); - U8* ptrB = getMutablePtr(Vec2i(0, m_size.y - 1 - y)); - memcpy(tmp.getPtr(), ptrA, scanBytes); - memcpy(ptrA, ptrB, scanBytes); - memcpy(ptrB, tmp.getPtr(), scanBytes); - } -} - -//------------------------------------------------------------------------ - -GLuint Image::createGLTexture(ImageFormat::ID desiredFormat, bool generateMipmaps) const -{ - // Select format. - - ImageFormat::ID formatID; - if (desiredFormat == ImageFormat::ID_Max) - formatID = m_format.getGLFormat(); - else - formatID = ImageFormat(desiredFormat).getGLFormat(); - - const ImageFormat::StaticFormat* sf = ImageFormat(formatID).getStaticFormat(); - FW_ASSERT(sf); - - // Image data not usable directly => convert. - - Image* converted = NULL; - const Image* img = this; - if (m_size.min() == 0 || m_format.getID() != formatID || m_stride != getBPP() * m_size.x) - { - converted = new Image(max(m_size, 1), formatID); - converted->set(*this); - img = converted; - } - - // create texture. - - GLContext::staticInit(); - - GLint oldTex = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); - - GLuint tex = 0; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, generateMipmaps); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (generateMipmaps) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // Uncomment to enable anisotropic filtering: -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, FW_S32_MAX); - - glTexImage2D(GL_TEXTURE_2D, 0, sf->glInternalFormat, - img->getSize().x, img->getSize().y, - 0, sf->glFormat, sf->glType, img->getPtr()); - - glBindTexture(GL_TEXTURE_2D, oldTex); - GLContext::checkErrors(); - - // Clean up. - - delete converted; - return tex; -} - -//------------------------------------------------------------------------ - -ImageFormat Image::chooseCudaFormat(CUDA_ARRAY_DESCRIPTOR* desc, ImageFormat::ID desiredFormat) const -{ -#if (!FW_USE_CUDA) - - FW_UNREF(desc); - FW_UNREF(desiredFormat); - fail("Image::chooseCudaFormat(): Built without FW_USE_CUDA!"); - return m_format; - -#else - - // Gather requirements. - - ImageFormat refFormat = m_format; - if (desiredFormat != ImageFormat::ID_Max) - refFormat = desiredFormat; - - int numChannels = min(refFormat.getNumChannels(), 4); - int channelBits = 0; - bool isFloat = false; - for (int i = 0; i < numChannels; i++) - { - const ImageFormat::Channel& chan = refFormat.getChannel(i); - channelBits = max(channelBits, chan.fieldSize); - isFloat = (chan.format == ImageFormat::ChannelFormat_Float); - } - - // Select format. - - CUarray_format datatype; - int wordSize; - - if (isFloat) datatype = CU_AD_FORMAT_FLOAT, wordSize = 4; - else if (channelBits <= 8) datatype = CU_AD_FORMAT_UNSIGNED_INT8, wordSize = 1; - else if (channelBits <= 16) datatype = CU_AD_FORMAT_UNSIGNED_INT16, wordSize = 2; - else datatype = CU_AD_FORMAT_UNSIGNED_INT32, wordSize = 4; - - ImageFormat formatA; // word per channel - ImageFormat formatB; // single word - - for (int i = 0; i < numChannels; i++) - { - const ImageFormat::Channel& ref = refFormat.getChannel(i); - - ImageFormat::Channel chan; - chan.Type = ref.Type; - chan.format = (isFloat) ? ImageFormat::ChannelFormat_Float : ref.format; - - chan.wordOfs = i * wordSize; - chan.wordSize = wordSize; - chan.fieldOfs = 0; - chan.fieldSize = wordSize * 8; - formatA.addChannel(chan); - - chan.wordOfs = 0; - chan.wordSize = wordSize * numChannels; - chan.fieldOfs = i * wordSize * 8; - chan.fieldSize = wordSize * 8; - formatB.addChannel(chan); - } - - // Fill in the descriptor. - - if (desc) - { - memset(desc, 0, sizeof(CUDA_ARRAY_DESCRIPTOR)); - desc->Width = m_size.x; - desc->Height = m_size.y; - desc->Format = datatype; - desc->NumChannels = numChannels; - } - return (formatB == refFormat) ? formatB : formatA; - -#endif -} - -//------------------------------------------------------------------------ - -CUarray Image::createCudaArray(ImageFormat::ID desiredFormat, ImageFormat* formatOut, CUDA_ARRAY_DESCRIPTOR* arrayDescOut) const -{ -#if (!FW_USE_CUDA) - - FW_UNREF(desiredFormat); - FW_UNREF(formatOut); - FW_UNREF(arrayDescOut); - fail("Image::createCudaArray(): Built without FW_USE_CUDA!"); - return NULL; - -#else - - // Choose format. - - CUDA_ARRAY_DESCRIPTOR arrayDesc; - ImageFormat cudaFormat = chooseCudaFormat(&arrayDesc, desiredFormat); - - // Image data not usable directly => convert. - - Image* converted = NULL; - const Image* img = this; - if (m_size.min() == 0 || m_format != cudaFormat) - { - converted = new Image(max(m_size, 1), cudaFormat); - converted->set(*this); - img = converted; - arrayDesc.Width = img->getSize().x; - arrayDesc.Height = img->getSize().y; - } - - // create CUDA array. - - CudaModule::staticInit(); - - CUarray cudaArray; - CudaModule::checkError("cuArrayCreate", cuArrayCreate(&cudaArray, &arrayDesc)); - - // Upload data. - - CUDA_MEMCPY2D copyDesc; - memset(©Desc, 0, sizeof(CUDA_MEMCPY2D)); - - copyDesc.srcXInBytes = 0; - copyDesc.srcY = 0; - copyDesc.srcMemoryType = CU_MEMORYTYPE_HOST; - copyDesc.srcHost = img->getPtr(); - copyDesc.srcPitch = img->getSize().x * img->getBPP(); - copyDesc.dstXInBytes = 0; - copyDesc.dstY = 0; - copyDesc.dstMemoryType = CU_MEMORYTYPE_ARRAY; - copyDesc.dstArray = cudaArray; - copyDesc.WidthInBytes = img->getSize().x * img->getBPP(); - copyDesc.Height = img->getSize().y; - - CudaModule::checkError("cuMemcpy2D", cuMemcpy2D(©Desc)); - - // Set output parameters. - - if (formatOut) - *formatOut = cudaFormat; - - if (arrayDescOut) - *arrayDescOut = arrayDesc; - - delete converted; - return cudaArray; - -#endif -} - -//------------------------------------------------------------------------ -// TODO: Figure out a cleaner interface for this. -// TODO: Expose wrap mode, filter mode, and flags. -// TODO: Support mipmapping. - -CUtexObject Image::createCudaTexObject(ImageFormat::ID desiredFormat, ImageFormat* formatOut, CUDA_ARRAY_DESCRIPTOR* arrayDescOut, CUarray* arrayOut) const -{ -#if (!FW_USE_CUDA) - - FW_UNREF(desiredFormat); - FW_UNREF(formatOut); - FW_UNREF(arrayDescOut); - FW_UNREF(arrayOut); - fail("Image::createCudaTexObject(): Built without FW_USE_CUDA!"); - return NULL; - -#else - - // create CUDA array. - - CUDA_ARRAY_DESCRIPTOR arrayDesc; - CUarray cudaArray = createCudaArray(desiredFormat, formatOut, &arrayDesc); - - // Fill in the resource descriptor. - - CUDA_RESOURCE_DESC resDesc; - memset(&resDesc, 0, sizeof(resDesc)); - - resDesc.resType = CU_RESOURCE_TYPE_ARRAY; - resDesc.res.array.hArray = cudaArray; - resDesc.flags = 0; - - // Fill in the texture descriptor. - - CUDA_TEXTURE_DESC texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - - texDesc.addressMode[0] = CU_TR_ADDRESS_MODE_WRAP; - texDesc.addressMode[1] = CU_TR_ADDRESS_MODE_WRAP; - texDesc.addressMode[2] = CU_TR_ADDRESS_MODE_WRAP; - texDesc.filterMode = CU_TR_FILTER_MODE_LINEAR; - texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; - texDesc.maxAnisotropy = 16; - texDesc.mipmapFilterMode = CU_TR_FILTER_MODE_LINEAR; - texDesc.mipmapLevelBias = 0.0f; - texDesc.minMipmapLevelClamp = 0.0f; - texDesc.maxMipmapLevelClamp = 16.0f; - - // create CUDA texture object. - - CudaModule::staticInit(); - - CUtexObject cudaTexObject; - CudaModule::checkError("cuTexObjectCreate", cuTexObjectCreate(&cudaTexObject, &resDesc, &texDesc, NULL)); - - // Set output parameters. - - if (arrayDescOut) - *arrayDescOut = arrayDesc; - - if (arrayOut) - *arrayOut = cudaArray; - - return cudaTexObject; - -#endif -} - -//------------------------------------------------------------------------ -// Implements a polyphase filter with round-down semantics from: -// -// Non-Power-of-Two Mipmapping -// (NVIDIA whitepaper) -// http://developer.nvidia.com/object/np2_mipmapping.html - -Image* Image::downscale2x(void) const -{ - // 1x1 or smaller => Bail out. - - int area = m_size.x * m_size.y; - if (area <= 1) - return NULL; - - // Choose filter dimensions. - - int fw = (m_size.x == 1) ? 1 : ((m_size.x & 1) == 0) ? 2 : 3; - int fh = (m_size.y == 1) ? 1 : ((m_size.y & 1) == 0) ? 2 : 3; - Vec2i resSize = max(m_size >> 1, 1); - int halfArea = area >> 1; - - // Allocate temporary scanline buffer and result image. - - Image tmp(Vec2i(m_size.x, fh), ImageFormat::ABGR_8888); - Image* res = new Image(resSize, ImageFormat::ABGR_8888); - U32* resPtr = (U32*)res->getMutablePtr(); - - // Process each scanline in the result. - - for (int y = 0; y < resSize.y; y++) - { - // Copy source scanlines into the temporary buffer. - - tmp.set(0, *this, Vec2i(0, y * 2), Vec2i(m_size.x, fh)); - - // Choose weights along the Y-axis. - - Vec3i wy(resSize.y); - if (fh == 3) - wy = Vec3i(resSize.y - y, resSize.y, y + 1); - - // Process each pixel in the result. - - for (int x = 0; x < resSize.x; x++) - { - // Choose weights along the X-axis. - - Vec3i wx(resSize.x); - if (fw == 3) - wx = Vec3i(resSize.x - x, resSize.x, x + 1); - - // Compute weighted average of pixel values. - - Vec4i sum = 0; - const U32* tmpPtr = (const U32*)tmp.getPtr(Vec2i(x * 2, 0)); - - for (int yy = 0; yy < fh; yy++) - { - for (int xx = 0; xx < fw; xx++) - { - U32 abgr = tmpPtr[xx]; - int weight = wx[xx] * wy[yy]; - sum.x += (abgr & 0xFF) * weight; - sum.y += ((abgr >> 8) & 0xFF) * weight; - sum.z += ((abgr >> 16) & 0xFF) * weight; - sum.w += (abgr >> 24) * weight; - } - tmpPtr += m_size.x; - } - - sum = (sum + halfArea) / area; - *resPtr++ = sum.x | (sum.y << 8) | (sum.z << 16) | (sum.w << 24); - } - } - return res; -} - -//------------------------------------------------------------------------ - -void Image::init(const Vec2i& size, const ImageFormat& format) -{ - FW_ASSERT(size.min() >= 0); - m_size = size; - m_format = format; - m_channelTmp.resize(m_format.getNumChannels()); -} - -//------------------------------------------------------------------------ - -void Image::createBuffer(void) -{ - m_stride = m_size.x * m_format.getBPP(); - m_buffer = new Buffer; - m_buffer->resize(m_stride * m_size.y); - m_ownBuffer = true; - m_offset = 0; -} - -//------------------------------------------------------------------------ - -void Image::replicatePixel(void) -{ - if (m_size.min() == 0) - return; - - int bpp = getBPP(); - U8* ptr = getMutablePtr(); - int scanBytes = m_size.x * bpp; - - for (int x = 1; x < m_size.x; x++) - memcpy(ptr + x * bpp, ptr, bpp); - for (int y = 1; y < m_size.y; y++) - memcpy(ptr + y * m_stride, ptr, scanBytes); -} - -//------------------------------------------------------------------------ - -bool Image::canBlitDirectly(const ImageFormat& format) -{ - switch (format.getID()) - { - case ImageFormat::R8_G8_B8: return true; - case ImageFormat::R8_G8_B8_A8: return true; - case ImageFormat::A8: return true; - case ImageFormat::XBGR_8888: return true; - case ImageFormat::ABGR_8888: return true; - - case ImageFormat::RGB_565: return true; - case ImageFormat::RGBA_5551: return true; - - case ImageFormat::RGB_Vec3f: return true; - case ImageFormat::RGBA_Vec4f: return true; - case ImageFormat::A_F32: return true; - - default: return false; - } -} - -//------------------------------------------------------------------------ - -bool Image::canBlitThruABGR(const ImageFormat& format) -{ - switch (format.getID()) - { - case ImageFormat::R8_G8_B8: return true; - case ImageFormat::R8_G8_B8_A8: return true; - case ImageFormat::A8: return true; - case ImageFormat::XBGR_8888: return true; - case ImageFormat::ABGR_8888: return true; - - case ImageFormat::RGB_565: return true; - case ImageFormat::RGBA_5551: return true; - - case ImageFormat::RGB_Vec3f: return false; - case ImageFormat::RGBA_Vec4f: return false; - case ImageFormat::A_F32: return false; - - default: return false; - } -} - -//------------------------------------------------------------------------ - -void Image::blit( - const ImageFormat& dstFormat, U8* dstPtr, S64 dstStride, - const ImageFormat& srcFormat, const U8* srcPtr, S64 srcStride, - const Vec2i& size) -{ - FW_ASSERT(size.min() >= 0); - if (size.min() == 0) - return; - - // Same format? - - if (dstFormat == srcFormat) - { - int scanBytes = size.x * dstFormat.getBPP(); - for (int y = 0; y < size.y; y++) - memcpy(dstPtr + dstStride * y, srcPtr + srcStride * y, scanBytes); - return; - } - - // To ABGR_8888? - - if (dstFormat.getID() == ImageFormat::ABGR_8888 && canBlitDirectly(srcFormat)) - { - for (int y = 0; y < size.y; y++) - blitToABGR((U32*)(dstPtr + dstStride * y), srcFormat, srcPtr + srcStride * y, size.x); - return; - } - - // From ABGR_8888? - - if (srcFormat.getID() == ImageFormat::ABGR_8888 && canBlitDirectly(dstFormat)) - { - for (int y = 0; y < size.y; y++) - blitFromABGR(dstFormat, dstPtr + dstStride * y, (const U32*)(srcPtr + srcStride * y), size.x); - return; - } - - // From integer-based format to another => convert thru ABGR_8888. - - if (canBlitDirectly(srcFormat) && canBlitDirectly(dstFormat) && canBlitThruABGR(srcFormat)) - { - Array tmp(NULL, size.x); - for (int y = 0; y < size.y; y++) - { - blitToABGR(tmp.getPtr(), srcFormat, srcPtr + srcStride * y, size.x); - blitFromABGR(dstFormat, dstPtr + dstStride * y, tmp.getPtr(), size.x); - } - return; - } - - // General case. - - S64 dstBPP = dstFormat.getBPP(); - S64 srcBPP = srcFormat.getBPP(); - Array dv(NULL, dstFormat.getNumChannels()); - Array sv(NULL, srcFormat.getNumChannels()); - Array map; - - for (int i = 0; i < dstFormat.getNumChannels(); i++) - { - ImageFormat::ChannelType t = dstFormat.getChannel(i).Type; - dv[i] = (t == ImageFormat::ChannelType_A) ? 1.0f : 0.0f; - int si = srcFormat.findChannel(t); - if (si != -1) - map.add(Vec2i(i, si)); - } - - for (int y = 0; y < size.y; y++) - { - U8* dstPixel = dstPtr + dstStride * y; - const U8* srcPixel = srcPtr + srcStride * y; - - for (int x = 0; x < size.x; x++) - { - getChannels(sv.getPtr(), srcPixel, srcFormat, 0, sv.getSize()); - for (int i = 0; i < map.getSize(); i++) - dv[map[i].x] = sv[map[i].y]; - setChannels(dstPixel, dv.getPtr(), dstFormat, 0, dv.getSize()); - - dstPixel += dstBPP; - srcPixel += srcBPP; - } - } -} - -//------------------------------------------------------------------------ - -void Image::blitToABGR(U32* dstPtr, const ImageFormat& srcFormat, const U8* srcPtr, int width) -{ - FW_ASSERT(width > 0); - FW_ASSERT(dstPtr && srcPtr); - FW_ASSERT(canBlitDirectly(srcFormat)); - - const U8* s8 = srcPtr; - const U16* s16 = (const U16*)srcPtr; - const Vec3f* sv3 = (const Vec3f*)srcPtr; - const Vec4f* sv4 = (const Vec4f*)srcPtr; - const F32* sf = (const F32*)srcPtr; - - switch (srcFormat.getID()) - { - case ImageFormat::R8_G8_B8: for (int x = width; x > 0; x--) { *dstPtr++ = s8[0] | (s8[1] << 8) | (s8[2] << 16) | 0xFF000000; s8 += 3; } break; - case ImageFormat::R8_G8_B8_A8: for (int x = width; x > 0; x--) { *dstPtr++ = s8[0] | (s8[1] << 8) | (s8[2] << 16) | (s8[3] << 24); s8 += 4; } break; - case ImageFormat::A8: for (int x = width; x > 0; x--) *dstPtr++ = *s8++ << 24; break; - case ImageFormat::XBGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - case ImageFormat::ABGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - - case ImageFormat::RGB_565: for (int x = width; x > 0; x--) { U16 v = *s16++; *dstPtr++ = RGB_565_TO_ABGR_8888(v); } break; - case ImageFormat::RGBA_5551: for (int x = width; x > 0; x--) { U16 v = *s16++; *dstPtr++ = RGBA_5551_TO_ABGR_8888(v); } break; - - case ImageFormat::RGB_Vec3f: for (int x = width; x > 0; x--) *dstPtr++ = Vec4f(*sv3++, 1.0f).toABGR(); break; - case ImageFormat::RGBA_Vec4f: for (int x = width; x > 0; x--) *dstPtr++ = (sv4++)->toABGR(); break; - case ImageFormat::A_F32: for (int x = width; x > 0; x--) *dstPtr++ = clamp((int)(*sf++ * 255.0f + 0.5f), 0x00, 0xFF) << 24; break; - - default: FW_ASSERT(false); break; - } -} - -//------------------------------------------------------------------------ - -void Image::blitFromABGR(const ImageFormat& dstFormat, U8* dstPtr, const U32* srcPtr, int width) -{ - FW_ASSERT(width > 0); - FW_ASSERT(dstPtr && srcPtr); - FW_ASSERT(canBlitDirectly(dstFormat)); - - U8* d8 = dstPtr; - U16* d16 = (U16*)dstPtr; - Vec3f* dv3 = (Vec3f*)dstPtr; - Vec4f* dv4 = (Vec4f*)dstPtr; - F32* df = (F32*)dstPtr; - - switch (dstFormat.getID()) - { - case ImageFormat::R8_G8_B8: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d8++ = (U8)v; *d8++ = (U8)(v >> 8); *d8++ = (U8)(v >> 16); } break; - case ImageFormat::R8_G8_B8_A8: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d8++ = (U8)v; *d8++ = (U8)(v >> 8); *d8++ = (U8)(v >> 16); *d8++ = (U8)(v >> 24); } break; - case ImageFormat::A8: for (int x = width; x > 0; x--) *d8++ = (U8)(*srcPtr++ >> 24); break; - case ImageFormat::XBGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - case ImageFormat::ABGR_8888: memcpy(dstPtr, srcPtr, width * sizeof(U32)); break; - - case ImageFormat::RGB_565: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d16++ = ABGR_8888_TO_RGB_565(v); } break; - case ImageFormat::RGBA_5551: for (int x = width; x > 0; x--) { U32 v = *srcPtr++; *d16++ = ABGR_8888_TO_RGBA_5551(v); } break; - - case ImageFormat::RGB_Vec3f: for (int x = width; x > 0; x--) *dv3++ = Vec4f::fromABGR(*srcPtr++).getXYZ(); break; - case ImageFormat::RGBA_Vec4f: for (int x = width; x > 0; x--) *dv4++ = Vec4f::fromABGR(*srcPtr++); break; - case ImageFormat::A_F32: for (int x = width; x > 0; x--) *df++ = (F32)(*srcPtr++ >> 24) / 255.0f; break; - - default: FW_ASSERT(false); break; - } -} - -//------------------------------------------------------------------------ - -void Image::getChannels(F32* values, const U8* pixelPtr, const ImageFormat& format, int first, int num) -{ - FW_ASSERT(num >= 0); - FW_ASSERT((values && pixelPtr) || !num); - FW_ASSERT(first >= 0 && first + num <= format.getNumChannels()); - - for (int i = 0; i < num; i++) - { - const ImageFormat::Channel& c = format.getChannel(i + first); - const U8* wordPtr = pixelPtr + c.wordOfs; - U32 field; - switch (c.wordSize) - { - case 1: field = *wordPtr; break; - case 2: field = *(const U16*)wordPtr; break; - case 4: field = *(const U32*)wordPtr; break; - default: FW_ASSERT(false); return; - } - field >>= c.fieldOfs; - - U32 mask = (1 << c.fieldSize) - 1; - switch (c.format) - { - case ImageFormat::ChannelFormat_Clamp: values[i] = (F32)(field & mask) / (F32)mask; break; - case ImageFormat::ChannelFormat_Int: values[i] = (F32)(field & mask); break; - case ImageFormat::ChannelFormat_Float: FW_ASSERT(c.fieldSize == 32); values[i] = bitsToFloat(field); break; - default: FW_ASSERT(false); return; - } - } -} - -//------------------------------------------------------------------------ - -void Image::setChannels(U8* pixelPtr, const F32* values, const ImageFormat& format, int first, int num) -{ - FW_ASSERT(num >= 0); - FW_ASSERT((pixelPtr && values) || !num); - FW_ASSERT(first >= 0 && first + num <= format.getNumChannels()); - - memset(pixelPtr, 0, format.getBPP()); - - for (int i = 0; i < num; i++) - { - const ImageFormat::Channel& c = format.getChannel(i + first); - U32 mask = (1 << c.fieldSize) - 1; - U32 field; - switch (c.format) - { - case ImageFormat::ChannelFormat_Clamp: field = min((U32)max(values[i] * (F32)mask + 0.5f, 0.0f), mask); break; - case ImageFormat::ChannelFormat_Int: field = min((U32)max(values[i] + 0.5f, 0.0f), mask); break; - case ImageFormat::ChannelFormat_Float: FW_ASSERT(c.fieldSize == 32); field = floatToBits(values[i]); break; - default: FW_ASSERT(false); return; - } - field <<= c.fieldOfs; - - U8* wordPtr = pixelPtr + c.wordOfs; - switch (c.wordSize) - { - case 1: *wordPtr |= (U8)field; break; - case 2: *(U16*)wordPtr |= (U16)field; break; - case 4: *(U32*)wordPtr |= field; break; - default: FW_ASSERT(false); return; - } - } -} - -//------------------------------------------------------------------------ -#endif \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.hpp b/Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.hpp deleted file mode 100644 index 75fd8715a..000000000 --- a/Framework/Source/Graphics/Model/Loaders/TeroBinaryCode/Image.hpp +++ /dev/null @@ -1,248 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -// #include "gpu/Buffer.hpp" -// #include "io/FileFormats.hpp" -#include -using namespace Falcor; - -namespace FW -{ -//------------------------------------------------------------------------ - using S32 = int32_t; - using U32 = uint32_t; - using U8 = uint8_t; -#define FW_ASSERT assert -#define FW_ARRAY_SIZE arraysize - -class ImageFormat -{ -public: - enum ID - { - R8_G8_B8 = 0, - R8_G8_B8_A8, - A8, - XBGR_8888, - ABGR_8888, - - RGB_565, - RGBA_5551, - - RGB_Vec3f, - RGBA_Vec4f, - A_F32, - - BGRA_8888, - BGR_888, - RG_88, - R8, - - // Compressed formats - S3TC_DXT1, - S3TC_DXT3, - S3TC_DXT5, - RGTC_R, - RGTC_RG, - - ID_Generic, - ID_Max - }; - - enum ChannelType // allows arbitrary values - { - ChannelType_R = 0, - ChannelType_G, - ChannelType_B, - ChannelType_A, - ChannelType_Generic, - - ChannelType_Max - }; - - enum ChannelFormat - { - ChannelFormat_Clamp = 0, // [0, 1] - ChannelFormat_Int, // [0, n[ - ChannelFormat_Float, // any - - ChannelFormat_Max - }; - - struct Channel - { - ChannelType Type; - ChannelFormat format; - S32 wordOfs; // bytes - S32 wordSize; // bytes - S32 fieldOfs; // bits - S32 fieldSize; // bits - }; - - struct StaticFormat - { - S32 bpp; - S32 numChannels; - Channel channels[4]; -// GLenum glInternalFormat; -// GLenum glFormat; -// GLenum glType; -// bool glLittleEndian; - }; - -public: - ImageFormat (void) { clear(); } - ImageFormat (ID id) : m_id(id) { FW_ASSERT(id >= 0 && id < ID_Generic); } - ImageFormat (const ImageFormat& other) { set(other); } - ~ImageFormat (void) {} - - ID getID (void) const; - const StaticFormat* getStaticFormat (void) const; - int getBPP (void) const; - int getNumChannels (void) const; - const Channel& getChannel (int idx) const; - int findChannel (ChannelType Type) const; - bool hasChannel (ChannelType Type) const { return (findChannel(Type) != -1); } - - void set (const ImageFormat& other); - void clear (void); - void addChannel (const Channel& channel); - - ID getGLFormat (void) const; - - ImageFormat& operator= (const ImageFormat& other) { set(other); return *this; } - bool operator== (const ImageFormat& other) const; - bool operator!= (const ImageFormat& other) const { return (!operator==(other)); } - -private: - static const StaticFormat s_staticFormats[]; - - mutable ID m_id; // ID_Max if unknown - S32 m_genericBPP; // only if m_id >= ID_Generic - std::vector m_genericChannels; // only if m_id >= ID_Generic -}; - -//------------------------------------------------------------------------ -#if 0 -class Image -{ -public: - Image (const Vec2i& size, const ImageFormat& format = ImageFormat::ABGR_8888) { init(size, format); createBuffer(); } - Image (const Vec2i& size, const ImageFormat& format, void* ptr, S64 stride); - Image (const Vec2i& size, const ImageFormat& format, Buffer& buffer, S64 ofs, S64 stride); - Image (const Image& other) { init(other.getSize(), other.getFormat()); createBuffer(); set(other); } - ~Image (void); - - bool contains (const Vec2i& pos, const Vec2i& size) const { return (pos.x >= 0 && pos.y >= 0 && pos.x + size.x <= m_size.x && pos.y + size.y <= m_size.y); } - - const Vec2i& getSize (void) const { return m_size; } - const ImageFormat& getFormat (void) const { return m_format; } - int getBPP (void) const { return m_format.getBPP(); } - S64 getStride (void) const { return m_stride; } - - Buffer& getBuffer (void) const { return *m_buffer; } - S64 getOffset (const Vec2i& pos = 0) const { FW_ASSERT(contains(pos, 0)); return m_offset + pos.x * getBPP() + pos.y * getStride(); } - const U8* getPtr (const Vec2i& pos = 0) const { return (const U8*)m_buffer->getPtr(getOffset(pos)); } - U8* getMutablePtr (const Vec2i& pos = 0) { return (U8*)m_buffer->getMutablePtr(getOffset(pos)); } - - void read (const ImageFormat& format, void* ptr, S64 stride, const Vec2i& pos, const Vec2i& size) const { FW_ASSERT(contains(pos, size)); blit(format, (U8*)ptr, stride, getFormat(), getPtr(pos), getStride(), size); } - void read (const ImageFormat& format, void* ptr, S64 stride) const { blit(format, (U8*)ptr, stride, getFormat(), getPtr(), getStride(), getSize()); } - void write (const ImageFormat& format, const void* ptr, S64 stride, const Vec2i& pos, const Vec2i& size) { FW_ASSERT(contains(pos, size)); blit(getFormat(), getMutablePtr(pos), getStride(), format, (const U8*)ptr, stride, size); } - void write (const ImageFormat& format, const void* ptr, S64 stride) { blit(getFormat(), getMutablePtr(), getStride(), format, (const U8*)ptr, stride, getSize()); } - void set (const Vec2i& dstPos, const Image& src, const Vec2i& srcPos, const Vec2i& size) { FW_ASSERT(contains(dstPos, size) && src.contains(srcPos, size)); blit(getFormat(), getMutablePtr(dstPos), getStride(), src.getFormat(), src.getPtr(srcPos), src.getStride(), size); } - void set (const Image& src) { blit(getFormat(), getMutablePtr(), getStride(), src.getFormat(), src.getPtr(), src.getStride(), Vec2i(min(getSize().x, src.getSize().x), min(getSize().y, src.getSize().y))); } - - void clear (U32 abgr = 0) { if (m_size.min() != 0) setABGR(0, abgr); replicatePixel(); } - void clear (const Vec4f& color) { if (m_size.min() != 0) setVec4f(0, color); replicatePixel(); } - - U32 getABGR (const Vec2i& pos) const; - void setABGR (const Vec2i& pos, U32 value); - Vec4f getVec4f (const Vec2i& pos) const; - void setVec4f (const Vec2i& pos, const Vec4f& value); - Vec3f getVec3f (const Vec2i& pos) const { return getVec4f(pos).getXYZ(); } - void setVec3f (const Vec2i& pos, const Vec3f& value) { setVec4f(pos, Vec4f(value, 1.0f)); } - - U32 getABGR (int x, int y) const { return getABGR(Vec2i(x, y)); } - void setABGR (int x, int y, U32 value) { setABGR(Vec2i(x, y), value); } - Vec4f getVec4f (int x, int y) const { return getVec4f(Vec2i(x, y)); } - void setVec4f (int x, int y, const Vec4f& value) { setVec4f(Vec2i(x, y), value); } - Vec3f getVec3f (int x, int y) const { return getVec3f(Vec2i(x, y)); } - void setVec3f (int x, int y, const Vec3f& value) { setVec3f(Vec2i(x, y), value); } - - void getChannels (F32* values, const Vec2i& pos, int first, int num) const { getChannels(values, getPtr(pos), getFormat(), first, num); } - void getChannels (F32* values, const Vec2i& pos) const { getChannels(values, getPtr(pos), getFormat(), 0, getFormat().getNumChannels()); } - const Array& getChannels (const Vec2i& pos) const { getChannels(m_channelTmp.getPtr(), getPtr(pos), getFormat(), 0, getFormat().getNumChannels()); return m_channelTmp; } - F32 getChannel (const Vec2i& pos, int idx) const { F32 res; getChannels(&res, getPtr(pos), getFormat(), idx, 1); return res; } - void setChannels (const Vec2i& pos, const F32* values, int first, int num) { setChannels(getMutablePtr(pos), values, getFormat(), first, num); } - void setChannels (const Vec2i& pos, const F32* values) { setChannels(getMutablePtr(pos), values, getFormat(), 0, getFormat().getNumChannels()); } - void setChannel (const Vec2i& pos, int idx, F32 value) { setChannels(getMutablePtr(pos), &value, getFormat(), idx, 1); } - - void flipX (void); - void flipY (void); - - GLuint createGLTexture (ImageFormat::ID desiredFormat = ImageFormat::ID_Max, bool generateMipmaps = true) const; - - ImageFormat chooseCudaFormat(CUDA_ARRAY_DESCRIPTOR* desc = NULL, ImageFormat::ID desiredFormat = ImageFormat::ID_Max) const; - CUarray createCudaArray (ImageFormat::ID desiredFormat = ImageFormat::ID_Max, ImageFormat* formatOut = NULL, CUDA_ARRAY_DESCRIPTOR* arrayDescOut = NULL) const; - CUtexObject createCudaTexObject(ImageFormat::ID desiredFormat = ImageFormat::ID_Max, ImageFormat* formatOut = NULL, CUDA_ARRAY_DESCRIPTOR* arrayDescOut = NULL, CUarray* arrayOut = NULL) const; - - Image* downscale2x (void) const; // Returns ImageFormat::ABGR_8888, or NULL if size <= 1x1. - - Image& operator= (const Image& other) { if (&other != this) set(other); return *this; } - -private: - void init (const Vec2i& size, const ImageFormat& format); - void createBuffer (void); - void replicatePixel (void); - - static bool canBlitDirectly (const ImageFormat& format); - static bool canBlitThruABGR (const ImageFormat& format); - - static void blit (const ImageFormat& dstFormat, U8* dstPtr, S64 dstStride, - const ImageFormat& srcFormat, const U8* srcPtr, S64 srcStride, - const Vec2i& size); - - static void blitToABGR (U32* dstPtr, const ImageFormat& srcFormat, const U8* srcPtr, int width); - static void blitFromABGR (const ImageFormat& dstFormat, U8* dstPtr, const U32* srcPtr, int width); - - static void getChannels (F32* values, const U8* pixelPtr, const ImageFormat& format, int first, int num); - static void setChannels (U8* pixelPtr, const F32* values, const ImageFormat& format, int first, int num); - -private: - Vec2i m_size; - ImageFormat m_format; - S64 m_stride; - Buffer* m_buffer; - bool m_ownBuffer; - S64 m_offset; - - mutable Array m_channelTmp; -}; -#endif -//------------------------------------------------------------------------ -} diff --git a/Framework/Source/Graphics/Model/Mesh.cpp b/Framework/Source/Graphics/Model/Mesh.cpp deleted file mode 100644 index 15f2399f4..000000000 --- a/Framework/Source/Graphics/Model/Mesh.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Mesh.h" -#include "Model.h" -#include "assimp/mesh.h" -#include "API/Buffer.h" -#include "AnimationController.h" -#include "API/VertexLayout.h" -#include "Graphics/Camera/Camera.h" -#include "Data/VertexAttrib.h" - -namespace Falcor -{ - uint32_t Mesh::sMeshCounter = 0; - Mesh::~Mesh() = default; - - Mesh::SharedPtr Mesh::create(const Vao::BufferVec& vertexBuffers, - uint32_t vertexCount, - const Buffer::SharedPtr& pIndexBuffer, - uint32_t indexCount, - const VertexLayout::SharedPtr& pLayout, - Vao::Topology topology, - const Material::SharedPtr& pMaterial, - const BoundingBox& boundingBox, - bool hasBones) - { - return SharedPtr(new Mesh(vertexBuffers, vertexCount, pIndexBuffer, indexCount, pLayout, topology, pMaterial, boundingBox, hasBones)); - } - - Mesh::Mesh(const Vao::BufferVec& vertexBuffers, - uint32_t vertexCount, - const Buffer::SharedPtr& pIndexBuffer, - uint32_t indexCount, - const VertexLayout::SharedPtr& pLayout, - Vao::Topology topology, - const Material::SharedPtr& pMaterial, - const BoundingBox& boundingBox, - bool hasBones) - : mId(sMeshCounter++) - , mIndexCount(indexCount) - , mVertexCount(vertexCount) - , mpMaterial(pMaterial) - , mBoundingBox(boundingBox) - , mHasBones(hasBones) - { - uint32_t VertsPerPrim = 3; - switch(topology) - { - case Vao::Topology::PointList: - VertsPerPrim = 1; - break; - case Vao::Topology::LineList: - VertsPerPrim = 2; - break; - case Vao::Topology::TriangleList: - VertsPerPrim = 3; - break; - default: - should_not_get_here(); - } - - mPrimitiveCount = mIndexCount / VertsPerPrim; - - mpVao = Vao::create(topology, pLayout, vertexBuffers, pIndexBuffer, ResourceFormat::R32Uint); - } - - void Mesh::resetGlobalIdCounter() - { - sMeshCounter = 0; - Material::resetGlobalIdCounter(); - } -} diff --git a/Framework/Source/Graphics/Model/Mesh.h b/Framework/Source/Graphics/Model/Mesh.h deleted file mode 100644 index 005a1efbc..000000000 --- a/Framework/Source/Graphics/Model/Mesh.h +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "glm/vec3.hpp" -#include "glm/mat4x4.hpp" -#include "API/VAO.h" -#include "API/RenderContext.h" -#include "Utils/AABB.h" -#include "Graphics/Material/Material.h" -#include "Graphics/Paths/MovableObject.h" - -namespace Falcor -{ - class Model; - class Buffer; - class Vao; - class VertexBufferLayout; - class Camera; - - class AssimpModelImporter; - class BinaryModelImporter; - class SimpleModelImporter; - - /** Class representing a single mesh - */ - class Mesh : public std::enable_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - /** create a new mesh - \param[in] VertexBuffers Vector of vertex buffer descriptors - \param[in] VertexCount Number of vertices in the vertex buffer - \param[in] pIndexBuffer Pointer to the index buffer - \param[in] IndexCount Number of indices in the index buffer - \param[in] Topology The primitive topology of the mesh - \param[in] pMaterial The material of the mesh - \param[in] BoundingBox The mesh's axis-aligned bounding-box - \param[in] bHasBones Indicates the the mesh uses bones for animation - */ - static SharedPtr create(const Vao::BufferVec& vertexBuffers, - uint32_t vertexCount, - const Buffer::SharedPtr& pIndexBuffer, - uint32_t indexCount, - const VertexLayout::SharedPtr& pLayout, - Vao::Topology topology, - const Material::SharedPtr& pMaterial, - const BoundingBox& boundingBox, - bool hasBones); - - /** Destructor - */ - ~Mesh(); - - /** Get the mesh's axis-aligned bounding-box in object space - */ - const BoundingBox& getBoundingBox() const { return mBoundingBox; } - - /** Get the number of vertices in the vertex buffer. If you want to draw, use GetIndexCount() instead. - */ - uint32_t getVertexCount() const { return mVertexCount; } - - /** Get the number of primitives. - */ - uint32_t getPrimitiveCount() const { return mPrimitiveCount; } - - /** Get the number of indices in the index buffer. Use this value when drawing the mesh. - */ - uint32_t getIndexCount() const { return mIndexCount; } - - /** Get a pointer to the mesh's material - */ - const Material::SharedPtr& getMaterial() const { return mpMaterial; } - - /** Does the mesh have bones? - */ - bool hasBones() const { return mHasBones; } - - /** Set the mesh's material. Can be used to override the material loaded with the model. - */ - void setMaterial(const Material::SharedPtr& pMaterial) { mpMaterial = pMaterial; } - - /** Get the vertex array object matching the mesh - */ - const Vao::SharedPtr& getVao() const { return mpVao; } - - /** Get global mesh ID - */ - const uint32_t getId() const { return mId; } - - /** Reset all global id counter of model, mesh and material - */ - static void resetGlobalIdCounter(); - - static const uint32_t kMaxBonesPerVertex = 4; ///> Max supported bones per vertex - - // TODO: Get mesh ID in file mesh was loaded from (temporary, fix better solution later) - const uint32_t getLoadId() const { return mLoadId; } - - protected: - friend AssimpModelImporter; - friend BinaryModelImporter; - friend SimpleModelImporter; - - private: - Mesh(const Vao::BufferVec& vertexBuffers, - uint32_t vertexCount, - const Buffer::SharedPtr& pIndexBuffer, - uint32_t indexCount, - const VertexLayout::SharedPtr& pLayout, - Vao::Topology topology, - const Material::SharedPtr& pMaterial, - const BoundingBox& boundingBox, - bool hasBones); - - static uint32_t sMeshCounter; - - uint32_t mId; - uint32_t mLoadId = 0; - uint32_t mIndexCount = 0; - uint32_t mVertexCount = 0; - uint32_t mPrimitiveCount = 0; - bool mHasBones = false; - Material::SharedPtr mpMaterial; - BoundingBox mBoundingBox; - Vao::SharedPtr mpVao; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/Model.cpp b/Framework/Source/Graphics/Model/Model.cpp deleted file mode 100644 index 60ed70b5a..000000000 --- a/Framework/Source/Graphics/Model/Model.cpp +++ /dev/null @@ -1,476 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Model.h" -#include "Loaders/AssimpModelImporter.h" -#include "Loaders/BinaryModelImporter.h" -#include "Loaders/BinaryModelExporter.h" -#include "Utils/Platform/OS.h" -#include "Mesh.h" -#include "AnimationController.h" -#include "Animation.h" -#include "API/Buffer.h" -#include "API/Texture.h" -#include "Graphics/TextureHelper.h" -#include "Utils/StringUtils.h" -#include "Graphics/Camera/Camera.h" -#include "API/VAO.h" -#include - -namespace Falcor -{ - - uint32_t Model::sModelCounter = 0; - const FileDialogFilterVec Model::kFileExtensionFilters = - { - {"obj"}, - {"bin"}, - {"dae"}, - {"x"}, - {"md5mesh"}, - {"ply"}, - {"fbx"}, - {"3ds"}, - {"blend"}, - {"ase"}, - {"ifc"}, - {"xgl"}, - {"zgl"}, - {"dxf"}, - {"lwo"}, - {"lws"}, - {"lxo"}, - {"stl"}, - {"x"}, - {"ac"}, - {"ms3d"}, - {"cob"}, - {"scn"}, - {"3d"}, - {"mdl"}, - {"mdl2"}, - {"pk3"}, - {"smd"}, - {"vta"}, - {"raw"}, - {"ter"}, - {"gltf"}, - {"glb"} - }; - - // Method to sort meshes - bool compareMeshes(const Mesh::SharedPtr& p1, const Mesh::SharedPtr& p2) - { - // This relies on the fact that Model keeps only unique copies of materials, so same material == same address. - // See GetOrAddMaterial() for more info - return p1->getMaterial() < p2->getMaterial(); - } - - Model::Model() : mId(sModelCounter++) - { - - } - - Model::Model(const Model& other) : mId(sModelCounter++) - { - mBoundingBox = other.mBoundingBox; - mRadius = other.mRadius; - mVertexCount = other.mVertexCount; - mIndexCount = other.mIndexCount; - mPrimitiveCount = other.mPrimitiveCount; - mMeshInstanceCount = other.mMeshInstanceCount; - mBufferCount = other.mBufferCount; - mMaterialCount = other.mMaterialCount; - mTextureCount = other.mTextureCount; - - mMeshes = other.mMeshes; - mpSkinningCache = other.mpSkinningCache; - if(other.mpAnimationController) - { - mpAnimationController = AnimationController::create(*other.mpAnimationController); - } - - mName = other.mName + "_copy"; - mFilename = other.mFilename; - } - - Model::~Model() = default; - - Model::SharedPtr Model::createFromFile(const char* filename, LoadFlags flags) - { - SharedPtr pModel = SharedPtr(new Model()); - bool res; - if(hasSuffix(filename, ".bin", false)) - { - res = BinaryModelImporter::import(*pModel, filename, flags); - } - else - { - res = AssimpModelImporter::import(*pModel, filename, flags); - } - - if(res) - { - pModel->calculateModelProperties(); - pModel->setFilename(filename); - - std::string name = getFilenameFromPath(filename); - size_t extPos = name.find_last_of('.'); - name = (extPos == std::string::npos) ? name : name.substr(0, extPos); - pModel->setName(name); - } - else - { - pModel = nullptr; - } - - return pModel; - } - - Model::SharedPtr Model::create() - { - return SharedPtr(new Model()); - } - - void Model::exportToBinaryFile(const std::string& filename) - { - if(hasSuffix(filename, ".bin", false) == false) - { - logWarning("Exporting model to binary file, but extension is not '.bin'. This will cause error when loading the file"); - } - - BinaryModelExporter::exportToFile(filename, this); - } - - void Model::calculateModelProperties() - { - mVertexCount = 0; - mIndexCount = 0; - mPrimitiveCount = 0; - mMeshInstanceCount = 0; - mBufferCount = 0; - mMaterialCount = 0; - mTextureCount = 0; - - std::set uniqueMaterials; - std::set uniqueTextures; - std::set uniqueBuffers; - - // Sort the meshes - sortMeshes(); - - vec3 modelMin = vec3(1e25f), modelMax = vec3(-1e25f); - - for(const auto& meshInstances : mMeshes) - { - // If we have a vector for a mesh, there should be at least one instance of it - assert(meshInstances.size() > 0); - - const auto& pMesh = meshInstances[0]->getObject(); - const uint32_t instanceCount = (uint32_t)meshInstances.size(); - - mVertexCount += pMesh->getVertexCount() * instanceCount; - mIndexCount += pMesh->getIndexCount() * instanceCount; - mPrimitiveCount += pMesh->getPrimitiveCount() * instanceCount; - mMeshInstanceCount += instanceCount; - - const Material* pMaterial = pMesh->getMaterial().get(); - - // Track material - uniqueMaterials.insert(pMaterial); - - // Track all of the material's textures - uniqueTextures.insert(pMaterial->getBaseColorTexture().get()); - uniqueTextures.insert(pMaterial->getSpecularTexture().get()); - uniqueTextures.insert(pMaterial->getEmissiveTexture().get()); - uniqueTextures.insert(pMaterial->getNormalMap().get()); - uniqueTextures.insert(pMaterial->getOcclusionMap().get()); - uniqueTextures.insert(pMaterial->getLightMap().get()); - uniqueTextures.insert(pMaterial->getHeightMap().get()); - - // Track the material's buffers - const auto& pVao = pMesh->getVao(); - for (uint32_t i = 0; i < (uint32_t)pVao->getVertexBuffersCount(); i++) - { - if (pVao->getVertexBuffer(i) != nullptr) - { - uniqueBuffers.insert(pVao->getVertexBuffer(i).get()); - } - } - - if (pVao->getIndexBuffer() != nullptr) - { - uniqueBuffers.insert(pVao->getIndexBuffer().get()); - } - - // Expand bounding box - for(uint32_t i = 0 ; i < instanceCount; i++) - { - const BoundingBox& meshBox = meshInstances[i]->getBoundingBox(); - - vec3 meshMin = meshBox.center - meshBox.extent; - vec3 meshMax = meshBox.center + meshBox.extent; - - modelMin = min(modelMin, meshMin); - modelMax = max(modelMax, meshMax); - } - } - - // Don't count nullptrs - uniqueTextures.erase(nullptr); - uniqueMaterials.erase(nullptr); - uniqueBuffers.erase(nullptr); - - mTextureCount = (uint32_t)uniqueTextures.size(); - mMaterialCount = (uint32_t)uniqueMaterials.size(); - mBufferCount = (uint32_t)uniqueBuffers.size(); - - mBoundingBox = BoundingBox::fromMinMax(modelMin, modelMax); - mRadius = glm::length(modelMin - modelMax) * 0.5f; - } - - bool Model::animate(double currentTime) - { - bool changed = false; - if(mpAnimationController) - { - mpAnimationController->animate(currentTime); - changed = true; // TODO: AnimationController::animate should return changed status. For now just mark it as always changed. - - if (update()) - { - changed = true; - } - } - return changed; - } - - bool Model::hasAnimations() const - { - return (getAnimationsCount() != 0); - } - - uint32_t Model::getAnimationsCount() const - { - return mpAnimationController ? (mpAnimationController->getAnimationCount()) : 0; - } - - uint32_t Model::getActiveAnimation() const - { - return mpAnimationController->getActiveAnimation(); - } - - const std::string& Model::getAnimationName(uint32_t animationID) const - { - assert(mpAnimationController); - assert(animationID < getAnimationsCount()); - return mpAnimationController->getAnimationName(animationID); - } - - void Model::setBindPose() - { - if(mpAnimationController) - { - mpAnimationController->setActiveAnimation(AnimationController::kBindPoseAnimationId); - } - } - - void Model::setActiveAnimation(uint32_t animationID) - { - assert(animationID < getAnimationsCount() || animationID == AnimationController::kBindPoseAnimationId); - mpAnimationController->setActiveAnimation(animationID); - } - - bool Model::hasBones() const - { - return (getBoneCount() != 0); - } - - uint32_t Model::getBoneCount() const - { - return mpAnimationController ? mpAnimationController->getBoneCount() : 0; - } - - const mat4* Model::getBoneMatrices() const - { - return mpAnimationController != nullptr ? mpAnimationController->getBoneMatrices().data() : nullptr; - } - - const mat4* Model::getBoneInvTransposeMatrices() const - { - return mpAnimationController != nullptr ? mpAnimationController->getBoneInvTransposeMatrices().data() : nullptr; - } - - void Model::bindSamplerToMaterials(const Sampler::SharedPtr& pSampler) - { - // Go over materials for all meshes and bind the sampler - for(auto& meshInstances : mMeshes) - { - meshInstances[0]->getObject()->getMaterial()->setSampler(pSampler); - } - } - - void Model::setAnimationController(AnimationController::UniquePtr pAnimController) - { - mpAnimationController = std::move(pAnimController); - } - - void Model::attachSkinningCache(SkinningCache::SharedPtr pSkinningCache) - { - mpSkinningCache = pSkinningCache; - } - - SkinningCache::SharedPtr Model::getSkinningCache() const - { - return mpSkinningCache; - } - - Vao::SharedPtr Model::getMeshVao(const Mesh* pMesh) const - { - assert(pMesh); - Vao::SharedPtr pVao = nullptr; - if (pMesh->hasBones()) - { - assert(mpSkinningCache); - pVao = mpSkinningCache->getVao(pMesh); - } - else - { - pVao = pMesh->getVao(); - } - assert(pVao); - return pVao; - } - - bool Model::update() - { - if (mpSkinningCache) - { - return mpSkinningCache->update(this); - } - return false; - } - - void Model::addMeshInstance(const Mesh::SharedPtr& pMesh, const glm::mat4& baseTransform) - { - int32_t meshID = -1; - - // Linear search from the end. Instances are usually added in order by mesh - for (int32_t i = (int32_t)mMeshes.size() - 1; i >= 0; i--) - { - if (mMeshes[i][0]->getObject() == pMesh) - { - meshID = i; - break; - } - } - - // If mesh not found, new mesh, add new instance vector - if (meshID == -1) - { - mMeshes.push_back(MeshInstanceList()); - meshID = (int32_t)mMeshes.size() - 1; - } - - mMeshes[meshID].push_back(MeshInstance::create(pMesh, baseTransform)); - } - - void Model::sortMeshes() - { - // Sort meshes by material ptr - auto matSortPred = [](MeshInstanceList& lhs, MeshInstanceList& rhs) - { - return lhs[0]->getObject()->getMaterial()->getId() < rhs[0]->getObject()->getMaterial()->getId(); - }; - - std::sort(mMeshes.begin(), mMeshes.end(), matSortPred); - } - - template - void removeNullElements(std::vector& Vec) - { - auto Pred = [](T& t) {return t == nullptr; }; - auto NewEnd = std::remove_if(Vec.begin(), Vec.end(), Pred); - Vec.erase(NewEnd, Vec.end()); - } - - void Model::deleteCulledMeshInstances(MeshInstanceList& meshInstances, const Camera *pCamera) - { - for (auto& instance : meshInstances) - { - if (pCamera->isObjectCulled(instance->getBoundingBox())) - { - // Remove mesh ptr reference - instance->mpObject = nullptr; - } - } - - // Remove culled instances - auto instPred = [](MeshInstance::SharedPtr& instance) { return instance->getObject() == nullptr; }; - auto instEnd = std::remove_if(meshInstances.begin(), meshInstances.end(), instPred); - meshInstances.erase(instEnd, meshInstances.end()); - } - - void Model::deleteCulledMeshes(const Camera* pCamera) - { - std::map usedMaterials; - std::map usedBuffers; - - // Loop over all the meshes and remove its instances - for(auto& meshInstances : mMeshes) - { - deleteCulledMeshInstances(meshInstances, pCamera); - - if(meshInstances.size() > 0) - { - // Mark the mesh's objects as used - usedMaterials[meshInstances[0]->getObject()->getMaterial().get()] = true; - const auto pVao = meshInstances[0]->getObject()->getVao(); - usedBuffers[pVao->getIndexBuffer().get()] = true; - - for(uint32_t i = 0 ; i < pVao->getVertexBuffersCount() ; i++) - { - usedBuffers[pVao->getVertexBuffer(i).get()] = true; - } - } - } - - // Remove unused meshes from the vector - auto pred = [](MeshInstanceList& meshInstances) { return meshInstances.size() == 0; }; - auto meshesEnd = std::remove_if(mMeshes.begin(), mMeshes.end(), pred); - mMeshes.erase(meshesEnd, mMeshes.end()); - - calculateModelProperties(); - } - - void Model::resetGlobalIdCounter() - { - sModelCounter = 0; - Mesh::resetGlobalIdCounter(); - } -} diff --git a/Framework/Source/Graphics/Model/Model.h b/Framework/Source/Graphics/Model/Model.h deleted file mode 100644 index 682504543..000000000 --- a/Framework/Source/Graphics/Model/Model.h +++ /dev/null @@ -1,313 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "glm/mat4x4.hpp" -#include "glm/vec3.hpp" -#include "Graphics/Model/Mesh.h" -#include "Graphics/Model/ObjectInstance.h" -#include "API/Sampler.h" -#include "Graphics/Model/AnimationController.h" -#include "Graphics/Model/SkinningCache.h" - -namespace Falcor -{ - class AssimpModelImporter; - class BinaryModelImporter; - class SimpleModelImporter; - class BinaryModelExporter; - class Buffer; - class Camera; - - /** Class representing a complete model object, including meshes, animations and materials - */ - - class Model : public std::enable_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - using MeshInstance = ObjectInstance; - using MeshInstanceList = std::vector; - - enum class LoadFlags - { - None, - DontGenerateTangentSpace = 0x1, ///< Do not attempt to generate tangents if they are missing - FindDegeneratePrimitives = 0x2, ///< Replace degenerate triangles/lines with lines/points. This can create a meshes with topology that wasn't present in the original model. - AssumeLinearSpaceTextures = 0x4, ///< By default, textures representing colors (diffuse/specular) are interpreted as sRGB data. Use this flag to force linear space for color textures. - DontMergeMeshes = 0x8, ///< Preserve the original list of meshes in the scene, don't merge meshes with the same material - BuffersAsShaderResource = 0x10, ///< Generate the VBs and IB with the shader-resource-view bind flag - RemoveInstancing = 0x20, ///< Flatten mesh instances - UseSpecGlossMaterials = 0x40, ///< Set materials to use Spec-Gloss shading model. Otherwise default is Metal-Rough for FBX, Spec-Gloss for OBJ. - UseMetalRoughMaterials = 0x80, ///< Set materials to use Metal-Rough shading model. Otherwise default is Metal-Rough for FBX, Spec-Gloss for OBJ. - }; - - /** Create a new model from file - */ - static SharedPtr createFromFile(const char* filename, LoadFlags flags = LoadFlags::None); - - static SharedPtr create(); - - static const FileDialogFilterVec kFileExtensionFilters; - - virtual ~Model(); - - /** Export the model to a binary file - */ - void exportToBinaryFile(const std::string& filename); - - /** Get the model radius, calculated based on bounding box size. - */ - float getRadius() const { return mRadius; } - - /** Get the model center. - */ - const glm::vec3& getCenter() const { return mBoundingBox.center; } - - /** Get the model's AABB. - */ - const BoundingBox& getBoundingBox() const { return mBoundingBox; } - - /** Get the number of vertices in the model. - */ - uint32_t getVertexCount() const { return mVertexCount; } - - /** Get the number of indices in the model. - */ - uint32_t getIndexCount() const { return mIndexCount; } - - /** Get the number of primitives in the model. - */ - uint32_t getPrimitiveCount() const { return mPrimitiveCount; } - - /** Get the number of meshes in the model. - */ - uint32_t getMeshCount() const { return uint32_t(mMeshes.size()); } - - /** Get the total number of mesh instances in the model. - */ - uint32_t getInstanceCount() const { return mMeshInstanceCount; } - - /** Get the number of unique textures in the model. - */ - uint32_t getTextureCount() const { return mTextureCount; } - - /** Get the number of unique materials in the model. - */ - uint32_t getMaterialCount() const { return mMaterialCount; } - - /** Get the number of unique buffers in the model. - */ - uint32_t getBufferCount() const { return mBufferCount; } - - /** Gets a mesh instance. - \param[in] meshID ID of the mesh - \param[in] instanceID ID of the instance - \return Mesh instance - */ - const MeshInstance::SharedPtr& getMeshInstance(uint32_t meshID, uint32_t instanceID) const { return mMeshes[meshID][instanceID]; } - - /** Gets a mesh. - \param[in] meshID ID of the mesh - \return Mesh object - */ - const Mesh::SharedPtr& getMesh(uint32_t meshID) const { return mMeshes[meshID][0]->getObject(); }; - - /** Gets how many instances exist of a mesh. - \param[in] meshID ID of the mesh - \return Number of instances - */ - uint32_t getMeshInstanceCount(uint32_t meshID) const { return meshID >= mMeshes.size() ? 0 : (uint32_t)(mMeshes[meshID].size()); } - - /** Adds a new mesh instance. - \param[in] pMesh Mesh geometry - \param[in] baseTransform Base transform for the instance - */ - void addMeshInstance(const Mesh::SharedPtr& pMesh, const glm::mat4& baseTransform); - - /** Check if the model contains animations. - */ - bool hasAnimations() const; - - /** Get the number of animations in the model. - */ - uint32_t getAnimationsCount() const; - - /** Animate the active animation. Use setActiveAnimation() to switch between different animations. - \param[in] currentTime The current global time - \return true if model has changed - */ - bool animate(double currentTime); - - /** Get the animation name from animation ID. - */ - const std::string& getAnimationName(uint32_t animationID) const; - - /** Turn animations off and use bind pose for rendering. - */ - void setBindPose(); - - /** Turn animation on and select active animation. Changing the active animation will cause the new animation to play from the beginning. - */ - void setActiveAnimation(uint32_t animationID); - - /** Get the active animation. - */ - uint32_t getActiveAnimation() const; - - /** Set the animation controller for the model. - */ - void setAnimationController(AnimationController::UniquePtr pAnimController); - - /** Attach a skinning cache to the model, or nullptr to detach. - When a cache is attached, the model will use compute shader based skinning with caching of the resulting skinned vertex buffers. - */ - void attachSkinningCache(SkinningCache::SharedPtr pSkinningCache); - - /** Get the skinning cache for the model, or nullptr if none. - */ - SkinningCache::SharedPtr getSkinningCache() const; - - /** Returns a vertex array object with skinned vertex buffers for skinned models, or the original vertex buffers otherwise. - This function requires a skinning cache to be attached to skinned models. - */ - Vao::SharedPtr getMeshVao(const Mesh* pMesh) const; - - /** Check if the model has bones. - */ - bool hasBones() const; - - /** Get the number of bone matrices. - */ - uint32_t getBoneCount() const; - - /** Get array of bone matrices. - \return If model has bones, return pointer to matrices in the current state of the animation. Otherwise nullptr. - */ - const mat4* getBoneMatrices() const; - - /** Get array of bones' inverse transpose matrices. - \return If model has bones, return pointer to matrices in the current state of the animation. Otherwise nullptr. - */ - const mat4* getBoneInvTransposeMatrices() const; - - /** Force all texture maps in all materials to use a specific texture sampler with one of their maps - \param[in] Type The map Type to bind the sampler with - */ - void bindSamplerToMaterials(const Sampler::SharedPtr& pSampler); - - /** Delete meshes from the model culled by the camera's frustum. - The function will also delete buffers, textures and materials not in use anymore. - */ - void deleteCulledMeshes(const Camera* pCamera); - - /** Name the model - */ - void setName(const std::string& Name) { mName = Name; } - - /** Get the model's name - */ - const std::string& getName() const { return mName; } - - /** Set the model's filename - */ - void setFilename(const std::string& filename) { mFilename = filename; } - - /** Get the model's filename - */ - const std::string& getFilename() const { return mFilename; } - - /** Get global ID of the model - */ - const uint32_t getId() const { return mId; } - - /** Reset all global id counter of model, mesh and material - */ - static void resetGlobalIdCounter(); - - - protected: - friend class SimpleModelImporter; - - Model(); - Model(const Model& other); - void sortMeshes(); - void deleteCulledMeshInstances(MeshInstanceList& meshInstances, const Camera *pCamera); - virtual bool update(); - - BoundingBox mBoundingBox; - float mRadius; - - uint32_t mVertexCount; - uint32_t mIndexCount; - uint32_t mPrimitiveCount; - uint32_t mMeshInstanceCount; - uint32_t mBufferCount; - uint32_t mMaterialCount; - uint32_t mTextureCount; - - uint32_t mId; - - std::vector mMeshes; // [Mesh][Instance] - - AnimationController::UniquePtr mpAnimationController; - SkinningCache::SharedPtr mpSkinningCache; - - std::string mName; - std::string mFilename; - - static uint32_t sModelCounter; - - void calculateModelProperties(); - }; - - enum_class_operators(Model::LoadFlags); - -#define flag_str(a) case Model::LoadFlags::a: return #a - inline std::string to_string(Model::LoadFlags f) - { - switch (f) - { - flag_str(None); - flag_str(DontGenerateTangentSpace); - flag_str(FindDegeneratePrimitives); - flag_str(AssumeLinearSpaceTextures); - flag_str(DontMergeMeshes); - flag_str(BuffersAsShaderResource); - flag_str(RemoveInstancing); - flag_str(UseSpecGlossMaterials); - default: - should_not_get_here(); - return ""; - } - } -#undef flag_str -} diff --git a/Framework/Source/Graphics/Model/ObjectInstance.h b/Framework/Source/Graphics/Model/ObjectInstance.h deleted file mode 100644 index 9e55108ca..000000000 --- a/Framework/Source/Graphics/Model/ObjectInstance.h +++ /dev/null @@ -1,336 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#pragma once - -#include "Graphics/Paths/MovableObject.h" -#include "Utils/AABB.h" -#include "glm/gtx/euler_angles.hpp" -#include "Utils/Math/FalcorMath.h" - -namespace Falcor -{ - class SceneRenderer; - class Model; - - /** Handles transformations for Mesh and Model instances. Primary transform is stored in the "Base" transform. An additional "Movable" - transform is applied after the Base transform can be set through the IMovableObject interface. This is currently used by paths. - */ - template - class ObjectInstance : public IMovableObject, public inherit_shared_from_this> - { - public: - using SharedPtr = std::shared_ptr>; - using SharedConstPtr = std::shared_ptr>; - - /** Constructs an object instance with a transform - \param[in] pObject Object to create an instance of - \param[in] baseTransform Base transform matrix of the instance - \param[in] name Name of the instance - \return A new instance of the object if pObject - */ - static SharedPtr create(const typename ObjectType::SharedPtr& pObject, const glm::mat4& baseTransform, const std::string& name = "") - { - assert(pObject); - return SharedPtr(new ObjectInstance(pObject, baseTransform, name)); - } - - /** Constructs an object instance with a transform - \param[in] pObject Object to create an instance of - \param[in] translation Base translation of the instance - \param[in] target Base look-at target of the instance - \param[in] up Base up vector of the instance - \param[in] scale Base scale of the instance - \param[in] name Name of the instance - \return A new instance of the object - */ - static SharedPtr create(const typename ObjectType::SharedPtr& pObject, const glm::vec3& translation, const glm::vec3& target, const glm::vec3& up, const glm::vec3& scale, const std::string& name = "") - { - return SharedPtr(new ObjectInstance(pObject, translation, target, up, scale, name)); - } - - /** Constructs an object instance with a transform - \param[in] pObject Object to create an instance of - \param[in] translation Base translation of the instance - \param[in] yawPitchRoll Rotation of the instance in radians - \param[in] scale Base scale of the instance - \param[in] name Name of the instance - \return A new instance of the object - */ - static SharedPtr create(const typename ObjectType::SharedPtr& pObject, const glm::vec3& translation, const glm::vec3& yawPitchRoll, const glm::vec3& scale, const std::string& name = "") - { - return SharedPtr(new ObjectInstance(pObject, translation, yawPitchRoll, scale, name)); - } - - /** Gets object for which this is an instance of - \return Object for this instance - */ - const typename ObjectType::SharedPtr& getObject() const { return mpObject; }; - - /** Sets visibility of this instance - \param[in] visible Visibility of this instance - */ - void setVisible(bool visible) { mVisible = visible; }; - - /** Gets whether this instance is visible - \return Whether this instance is visible - */ - bool isVisible() const { return mVisible; }; - - /** Gets instance name - \return Instance name - */ - const std::string& getName() const { return mName; } - - /** Sets instance name - \param[in] name Instance name - */ - void setName(const std::string& name) { mName = name; } - - /** Sets position/translation of the instance - \param[in] translation Instance translation - \param[in] updateLookAt If true, translates the look-at target as well to maintain rotation - */ - void setTranslation(const glm::vec3& translation, bool updateLookAt) - { - if (updateLookAt) - { - glm::vec3 toLookAt = mBase.target - mBase.translation; - mBase.target = translation + toLookAt; - } - - mBase.translation = translation; - mBase.matrixDirty = true; - }; - - /** Gets the position/translation of the instance - \return Translation of the instance - */ - const glm::vec3& getTranslation() const { return mBase.translation; }; - - /** Sets scale of the instance - \param[in] scaling Instance scale - */ - void setScaling(const glm::vec3& scaling) { mBase.scale = scaling; mBase.matrixDirty = true; } - - /** Gets scale of the instance - \return Scale of the instance - */ - const glm::vec3& getScaling() const { return mBase.scale; } - - /** Sets orientation of the instance - \param[in] yawPitchRoll Yaw-Pitch-Roll rotation in radians - */ - void setRotation(const glm::vec3& yawPitchRoll) - { - // Construct matrix from angles and take upper 3x3 - const glm::mat3 rotMtx(glm::yawPitchRoll(yawPitchRoll[0], yawPitchRoll[1], yawPitchRoll[2])); - - // Get look-at info - mBase.up = rotMtx[1]; - mBase.target = mBase.translation + rotMtx[2]; // position + forward - - mBase.matrixDirty = true; - } - - /** Gets rotation for the instance - \return Yaw-Pitch-Roll rotations in radians - */ - glm::vec3 getRotation() const - { - glm::vec3 result; - - glm::mat4 rotationMtx = createMatrixFromLookAt(mBase.translation, mBase.target, mBase.up); - glm::extractEulerAngleXYZ(rotationMtx, result[1], result[0], result[2]); // YawPitchRoll is YXZ - - return result; - } - - /** Sets the up vector orientation - */ - void setUpVector(const glm::vec3& up) { mBase.up = glm::normalize(up); mBase.matrixDirty = true; } - - /** Sets the look-at target - */ - void setTarget(const glm::vec3& target) { mBase.target = target; mBase.matrixDirty = true; } - - /** Gets the up vector of the instance - \return Up vector - */ - const glm::vec3& getUpVector() const { return mBase.up; } - - /** Gets look-at target of the instance's orientation - \return Look-at target position - */ - const glm::vec3& getTarget() const { return mBase.target; } - - /** Gets the transform matrix - \return Transform matrix - */ - const glm::mat4& getTransformMatrix() const - { - updateInstanceProperties(); - return mFinalTransformMatrix; - } - - const glm::mat4& getPrevTransformMatrix() const - { - updateInstanceProperties(); - return mPrevFinalTransformMatrix; - } - - /** Gets the bounding box - \return Bounding box - */ - const BoundingBox& getBoundingBox() const - { - updateInstanceProperties(); - return mBoundingBox; - } - - /** IMovableObject interface - */ - virtual void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override - { - mMovable.translation = position; - mMovable.target = target; - mMovable.up = up; - mMovable.scale = glm::vec3(1.0f); - mMovable.matrixDirty = true; - } - - SharedPtr shared_from_this() - { - return inherit_shared_from_this < IMovableObject, ObjectInstance>::shared_from_this(); - } - - SharedConstPtr shared_from_this() const - { - return inherit_shared_from_this < IMovableObject, ObjectInstance>::shared_from_this(); - } - private: - - void updateInstanceProperties() const - { - if (mBase.matrixDirty || mMovable.matrixDirty) - { - if (mBase.matrixDirty) - { - mBase.matrix = calculateTransformMatrix(mBase.translation, mBase.target, mBase.up, mBase.scale); - mBase.matrixDirty = false; - } - - if (mMovable.matrixDirty) - { - mPrevMovable = mMovable; - mMovable.matrix = calculateTransformMatrix(mMovable.translation, mMovable.target, mMovable.up, mMovable.scale); - mMovable.matrixDirty = false; - } - - mFinalTransformMatrix = mMovable.matrix * mBase.matrix; - mPrevFinalTransformMatrix = mPrevMovable.matrix * mBase.matrix; - - mBoundingBox = mpObject->getBoundingBox().transform(mFinalTransformMatrix); - } - } - - static glm::mat4 calculateTransformMatrix(const glm::vec3& translation, const glm::vec3& target, const glm::vec3& up, const glm::vec3& scale) - { - glm::mat4 translationMtx = glm::translate(glm::mat4(), translation); - glm::mat4 rotationMtx = createMatrixFromLookAt(translation, target, up); - glm::mat4 scalingMtx = glm::scale(glm::mat4(), scale); - - return translationMtx * rotationMtx * scalingMtx; - } - - static glm::mat4 calculateTransformMatrix(const glm::vec3& translation, const glm::vec3& yawPitchRoll, const glm::vec3& scale) - { - glm::mat4 translationMtx = glm::translate(glm::mat4(), translation); - glm::mat4 rotationMtx = glm::yawPitchRoll(yawPitchRoll[0], yawPitchRoll[1], yawPitchRoll[2]); - glm::mat4 scalingMtx = glm::scale(glm::mat4(), scale); - - return translationMtx * rotationMtx * scalingMtx; - } - - ObjectInstance(const typename ObjectType::SharedPtr& pObject, const std::string& name) - : mpObject(pObject), mName(name) { } - - ObjectInstance(const typename ObjectType::SharedPtr& pObject, const glm::mat4& baseTransform, const std::string& name) - : ObjectInstance(pObject, name) - { - // #TODO Decompose matrix - - mBase.matrix = baseTransform; - mBase.matrixDirty = false; - } - - ObjectInstance(const typename ObjectType::SharedPtr& pObject, const glm::vec3& translation, const glm::vec3& target, const glm::vec3& up, const glm::vec3& scale, const std::string& name = "") - : ObjectInstance(pObject, name) - { - mBase.translation = translation; - mBase.target = target; - mBase.up = up; - mBase.scale = scale; - } - - ObjectInstance(const typename ObjectType::SharedPtr& pObject, const glm::vec3& translation, const glm::vec3& yawPitchRoll, const glm::vec3& scale, const std::string& name = "") - : ObjectInstance(pObject, name) - { - mBase.translation = translation; - setRotation(yawPitchRoll); - mBase.scale = scale; - } - - friend class Model; - - std::string mName; - bool mVisible = true; - - typename ObjectType::SharedPtr mpObject; - - struct Transform - { - glm::vec3 translation; - glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 target = glm::vec3(0.0f, 0.0f, 1.0f); - glm::vec3 scale = glm::vec3(1.0f); - - // Matrix containing the above transforms - glm::mat4 matrix; - bool matrixDirty = true; - }; - - mutable Transform mBase; - mutable Transform mMovable; - mutable Transform mPrevMovable; - - mutable glm::mat4 mFinalTransformMatrix; - mutable glm::mat4 mPrevFinalTransformMatrix; - mutable BoundingBox mBoundingBox; - }; -} diff --git a/Framework/Source/Graphics/Model/SkinningCache.cpp b/Framework/Source/Graphics/Model/SkinningCache.cpp deleted file mode 100644 index 8c5fc60fc..000000000 --- a/Framework/Source/Graphics/Model/SkinningCache.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "SkinningCache.h" -#include "API/Device.h" -#include "Data/VertexAttrib.h" -#include "Graphics/Model/Model.h" - -namespace Falcor -{ - static const char* kShaderFilenameSkinning = "Data/Framework/Shaders/ComputeSkinning.cs.slang"; - static const char* kPerModelCbName = "PerModelCB"; - static const char* kPerMeshCbName = "PerMeshCB"; - - static const uint32_t kGroupSize = 256; // threads per group - - SkinningCache::SharedPtr SkinningCache::create() - { - SharedPtr ptr = SharedPtr(new SkinningCache()); - return ptr->init() ? ptr : nullptr; - } - - bool SkinningCache::update(const Model* pModel) - { - bool changed = false; - if (pModel->hasBones()) - { - RenderContext* pRenderContext = gpDevice->getRenderContext(); - pRenderContext->pushComputeState(mSkinningPass.pState); - pRenderContext->pushComputeVars(mSkinningPass.pVars); - - setPerModelData(pModel); - - for (uint32_t meshId = 0; meshId < pModel->getMeshCount(); meshId++) - { - const Mesh* pMesh = pModel->getMesh(meshId).get(); - if (pMesh->hasBones()) - { - createVertexBuffers(pMesh); - - // Bind resources - setPerMeshData(pMesh); - - // Execute - // TODO: Using 1D dispatch for simplicity, which limits us to 64k x 256 = 16M vertices with 256 in group size. Fix if needed. - assert(pMesh->getVertexCount() <= 16*1024*1024); - uint32_t numGroups = (pMesh->getVertexCount() + kGroupSize - 1) / kGroupSize; - pRenderContext->dispatch(numGroups, 1, 1); - - changed = true; - } - } - - pRenderContext->popComputeVars(); - pRenderContext->popComputeState(); - } - return changed; - } - - Vao::SharedPtr SkinningCache::getVao(const Mesh* pMesh) const - { - auto it = mSkinnedBuffers.find(pMesh); - if (it != mSkinnedBuffers.end()) - { - return it->second.pVao; - } - return nullptr; - } - - bool SkinningCache::init() - { - // Create shaders - mSkinningPass.pProgram = ComputeProgram::createFromFile(kShaderFilenameSkinning, "main"); - assert(mSkinningPass.pProgram); - mSkinningPass.pVars = ComputeVars::create(mSkinningPass.pProgram->getReflector()); - - // Create state - mSkinningPass.pState = ComputeState::create(); - mSkinningPass.pState->setProgram(mSkinningPass.pProgram); - - const ParameterBlockReflection* pBlock = mSkinningPass.pProgram->getReflector()->getDefaultParameterBlock().get(); - initVariableOffsets(pBlock); - initMeshBufferLocations(pBlock); - - return true; - } - - void SkinningCache::initVariableOffsets(const ParameterBlockReflection* pBlock) - { - if (mVariableOffsets.bonesOffset == ConstantBuffer::kInvalidOffset) - { - const ReflectionVar* pVar = pBlock->getResource(kPerModelCbName).get(); - - if (pVar != nullptr) - { - assert(pVar->getType()->asResourceType()->getType() == ReflectionResourceType::Type::ConstantBuffer); - const ReflectionType* pType = pVar->getType().get(); - - assert(pType->findMember("gBoneMat[0]")->getType()->asBasicType()->isRowMajor() == true); // We copy into CBs as row-major - assert(pType->findMember("gInvTransposeBoneMat[0]")->getType()->asBasicType()->isRowMajor() == true); - assert(pType->findMember("gBoneMat")->getType()->getTotalArraySize() >= MAX_BONES); - assert(pType->findMember("gInvTransposeBoneMat")->getType()->getTotalArraySize() >= MAX_BONES); - - mVariableOffsets.bonesOffset = pType->findMember("gBoneMat[0]")->getOffset(); - mVariableOffsets.bonesInvTransposeOffset = pType->findMember("gInvTransposeBoneMat[0]")->getOffset(); - } - } - } - - void SkinningCache::initMeshBufferLocations(const ParameterBlockReflection* pBlock) - { - // Input - mMeshBufferLocations.position = pBlock->getResourceBinding("gPositions"); - mMeshBufferLocations.normal = pBlock->getResourceBinding("gNormals"); - mMeshBufferLocations.bitangent = pBlock->getResourceBinding("gBitangents"); - mMeshBufferLocations.boneWeights = pBlock->getResourceBinding("gBoneWeights"); - mMeshBufferLocations.boneIds = pBlock->getResourceBinding("gBoneIds"); - // Output - mMeshBufferLocations.positionOut = pBlock->getResourceBinding("gSkinnedPositions"); - mMeshBufferLocations.prevPositionOut = pBlock->getResourceBinding("gSkinnedPrevPositions"); - mMeshBufferLocations.normalOut = pBlock->getResourceBinding("gSkinnedNormals"); - mMeshBufferLocations.bitangentOut = pBlock->getResourceBinding("gSkinnedBitangents"); - } - - static Buffer::SharedPtr createVertexBuffer(uint32_t vertexLoc, const Vao* pVao, std::vector& pVBs) - { - Buffer::SharedPtr pBuffer = nullptr; - const auto& elemDesc = pVao->getElementIndexByLocation(vertexLoc); - if (elemDesc.vbIndex != Vao::ElementDesc::kInvalidIndex) - { - assert(elemDesc.vbIndex < pVao->getVertexBuffersCount()); - size_t size = pVao->getVertexBuffer(elemDesc.vbIndex)->getSize(); - pBuffer = Buffer::create(size, Resource::BindFlags::Vertex | Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); - pVBs[elemDesc.vbIndex] = pBuffer; - } - return pBuffer; - } - - // Create vertex buffers for the skinned vertices of a Mesh if they do not already exist. - void SkinningCache::createVertexBuffers(const Mesh* pMesh) - { - auto it = mSkinnedBuffers.find(pMesh); - if (it == mSkinnedBuffers.end()) - { - const Vao* pVao = pMesh->getVao().get(); - const uint32_t bufferCount = pVao->getVertexBuffersCount(); - - // Create buffers for skinned vertices - std::vector pVBs(bufferCount + 1); - - Buffer::SharedPtr pPosBuffer = createVertexBuffer(VERTEX_POSITION_LOC, pVao, pVBs); - createVertexBuffer(VERTEX_NORMAL_LOC, pVao, pVBs); - createVertexBuffer(VERTEX_BITANGENT_LOC, pVao, pVBs); - - // Copy non-skinned buffers from the original VAO. - for (uint32_t i = 0; i < bufferCount; i++) - { - if (pVBs[i] == nullptr) - { - pVBs[i] = pVao->getVertexBuffer(i); - } - } - - // Create duplicate of position buffer to hold positions for the previous frame - pVBs[bufferCount] = Buffer::create(pPosBuffer->getSize(), pPosBuffer->getBindFlags(), pPosBuffer->getCpuAccess()); - - VertexBufferLayout::SharedPtr pVbLayout = VertexBufferLayout::create(); - pVbLayout->addElement(VERTEX_PREV_POSITION_NAME, 0, ResourceFormat::RGB32Float, 1, VERTEX_PREV_POSITION_LOC); - - // Create new vertex layout including the additional buffer - VertexLayout::SharedPtr pLayout = VertexLayout::create(); - assert(pVao->getVertexLayout()->getBufferCount() == bufferCount); - for (uint32_t i = 0; i < bufferCount; i++) - { - pLayout->addBufferLayout(i, pVao->getVertexLayout()->getBufferLayout(i)); - } - pLayout->addBufferLayout(bufferCount, pVbLayout); - - // Create VAO for skinned mesh. - VertexBuffers buffers; - buffers.pVao = Vao::create(pVao->getPrimitiveTopology(), pLayout, pVBs, pVao->getIndexBuffer(), pVao->getIndexBufferFormat()); - - mSkinnedBuffers[pMesh] = buffers; - } - } - - void SkinningCache::setPerModelData(const Model* pModel) - { - // Set bones - assert(pModel->hasBones()); - ConstantBuffer::SharedPtr pCB = mSkinningPass.pVars->getConstantBuffer(kPerModelCbName); - if (pCB) - { - assert(pModel->getBoneCount() <= MAX_BONES); - pCB->setVariableArray(mVariableOffsets.bonesOffset, pModel->getBoneMatrices(), pModel->getBoneCount()); - pCB->setVariableArray(mVariableOffsets.bonesInvTransposeOffset, pModel->getBoneInvTransposeMatrices(), pModel->getBoneCount()); - } - } - - static bool setVertexBuffer(ParameterBlockReflection::BindLocation bindLocation, uint32_t vertexLoc, const Vao* pVao, ProgramVars* pVars, ResourceFormat expectedFormat = ResourceFormat::Unknown) - { - assert(bindLocation.setIndex != ProgramReflection::kInvalidLocation); - const auto& elemDesc = pVao->getElementIndexByLocation(vertexLoc); - if (elemDesc.elementIndex == Vao::ElementDesc::kInvalidIndex) - { - pVars->getDefaultBlock()->setSrv(bindLocation, 0, nullptr); - } - else - { - assert(elemDesc.elementIndex == 0); - assert(elemDesc.vbIndex != Vao::ElementDesc::kInvalidIndex); - assert(expectedFormat == ResourceFormat::Unknown || pVao->getVertexLayout()->getBufferLayout(elemDesc.vbIndex)->getElementFormat(elemDesc.elementIndex) == expectedFormat); - pVars->getDefaultBlock()->setSrv(bindLocation, 0, pVao->getVertexBuffer(elemDesc.vbIndex)->getSRV()); - return true; - } - return false; - } - - // TODO: avoid duplication, the functions only differ by Srv/Uav - static bool setVertexBufferUAV(ParameterBlockReflection::BindLocation bindLocation, uint32_t vertexLoc, const Vao* pVao, ProgramVars* pVars, ResourceFormat expectedFormat = ResourceFormat::Unknown) - { - assert(bindLocation.setIndex != ProgramReflection::kInvalidLocation); - const auto& elemDesc = pVao->getElementIndexByLocation(vertexLoc); - if (elemDesc.elementIndex == Vao::ElementDesc::kInvalidIndex) - { - pVars->getDefaultBlock()->setUav(bindLocation, 0, nullptr); - } - else - { - assert(elemDesc.elementIndex == 0); - assert(elemDesc.vbIndex != Vao::ElementDesc::kInvalidIndex); - assert(expectedFormat == ResourceFormat::Unknown || pVao->getVertexLayout()->getBufferLayout(elemDesc.vbIndex)->getElementFormat(elemDesc.elementIndex) == expectedFormat); - pVars->getDefaultBlock()->setUav(bindLocation, 0, pVao->getVertexBuffer(elemDesc.vbIndex)->getUAV()); - return true; - } - return false; - } - - void SkinningCache::setPerMeshData(const Mesh* pMesh) - { - ProgramVars* pVars = mSkinningPass.pVars.get(); - - // Set constants - assert(pMesh->hasBones()); - ConstantBuffer::SharedPtr pCB = pVars->getConstantBuffer(kPerMeshCbName); - if (pCB) - { - pCB["gNumVertices"] = pMesh->getVertexCount(); - } - - // Bind input vertex buffers - const Vao* pVao = pMesh->getVao().get(); - bool hasPos = setVertexBuffer(mMeshBufferLocations.position, VERTEX_POSITION_LOC, pVao, pVars, ResourceFormat::RGB32Float); - bool hasNormal = setVertexBuffer(mMeshBufferLocations.normal, VERTEX_NORMAL_LOC, pVao, pVars, ResourceFormat::RGB32Float); - bool hasBitangent = setVertexBuffer(mMeshBufferLocations.bitangent, VERTEX_BITANGENT_LOC, pVao, pVars, ResourceFormat::RGB32Float); - bool hasBoneWeight = setVertexBuffer(mMeshBufferLocations.boneWeights, VERTEX_BONE_WEIGHT_LOC, pVao, pVars, ResourceFormat::RGBA32Float); - bool hasBoneId = setVertexBuffer(mMeshBufferLocations.boneIds, VERTEX_BONE_ID_LOC, pVao, pVars, ResourceFormat::RGBA8Uint); - assert(hasPos && hasBoneWeight && hasBoneId); - - // Bind output vertex buffers. Note some of the buffers may be nullptr. - const auto& it = mSkinnedBuffers.find(pMesh); - assert(it != mSkinnedBuffers.end()); - - const Vao* pVaoOut = it->second.pVao.get(); - setVertexBufferUAV(mMeshBufferLocations.positionOut, VERTEX_POSITION_LOC, pVaoOut, pVars); - setVertexBufferUAV(mMeshBufferLocations.prevPositionOut, VERTEX_PREV_POSITION_LOC, pVaoOut, pVars); - setVertexBufferUAV(mMeshBufferLocations.normalOut, VERTEX_NORMAL_LOC, pVaoOut, pVars); - setVertexBufferUAV(mMeshBufferLocations.bitangentOut, VERTEX_BITANGENT_LOC, pVaoOut, pVars); - - if (hasNormal) mSkinningPass.pProgram->addDefine("HAS_NORMAL"); - else mSkinningPass.pProgram->removeDefine("HAS_NORMAL"); - - if (hasBitangent) mSkinningPass.pProgram->addDefine("HAS_BITANGENT"); - else mSkinningPass.pProgram->removeDefine("HAS_BITANGENT"); - - if (!it->second.valid) mSkinningPass.pProgram->addDefine("FIRST_FRAME"); - else mSkinningPass.pProgram->removeDefine("FIRST_FRAME"); - - it->second.valid = true; - } -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Model/SkinningCache.h b/Framework/Source/Graphics/Model/SkinningCache.h deleted file mode 100644 index 80bad8c88..000000000 --- a/Framework/Source/Graphics/Model/SkinningCache.h +++ /dev/null @@ -1,125 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "API/RenderContext.h" - -namespace Falcor -{ - class Model; - class Mesh; - - /** Cache for skinned vertex buffers for one or more models. - - Compute shader based skinning that caches the generated vertex buffers. - This allows skinning to be done asynchronously before rendering. - It also allows updating skinning at a lower frequency than the frame rate, - and it simplifies the scene renderer as it does not have to deal with skinning. - - TODOs: - - 1) The class handles skinning of positions, normals, and bitangents. - We might want to generalize it and allow the user to override the shader - to output additional skinned vertex buffers. - - 2) Currently, a single set of skinned vertex buffers is stored per mesh. - We could extend that to hold multiple buffers to cache entire animations. - - 3) We could also extend it to hold skinned buffers per mesh instance, to enable - mesh instances to be animated separately. - - 4) Provide metric on amount of change to guide choice of BVH rebuild/refit for ray tracing purposes. - - */ - class SkinningCache : public std::enable_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - virtual ~SkinningCache() = default; - - static SharedPtr create(); - - /** Create/update skinned vertex buffers for model. - */ - bool update(const Model* pModel); - - /** Returns the vertex array object for pMesh containing skinned vertex buffers if it exists. - */ - Vao::SharedPtr getVao(const Mesh* pMesh) const; - - protected: - SkinningCache() = default; - - bool init(); - void initVariableOffsets(const ParameterBlockReflection* pBlock); - void initMeshBufferLocations(const ParameterBlockReflection* pBlock); - void createVertexBuffers(const Mesh* pMesh); - void setPerModelData(const Model* pModel); - void setPerMeshData(const Mesh* pMesh); - - struct VertexBuffers - { - Vao::SharedPtr pVao; - bool valid = false; - }; - - struct VariableOffsets - { - size_t bonesOffset = ConstantBuffer::kInvalidOffset; - size_t bonesInvTransposeOffset = ConstantBuffer::kInvalidOffset; - }; - - struct MeshBufferLocations - { - // Input - ParameterBlockReflection::BindLocation position; - ParameterBlockReflection::BindLocation normal; - ParameterBlockReflection::BindLocation bitangent; - ParameterBlockReflection::BindLocation boneWeights; - ParameterBlockReflection::BindLocation boneIds; - // Output - ParameterBlockReflection::BindLocation positionOut; - ParameterBlockReflection::BindLocation prevPositionOut; - ParameterBlockReflection::BindLocation normalOut; - ParameterBlockReflection::BindLocation bitangentOut; - }; - - VariableOffsets mVariableOffsets; - MeshBufferLocations mMeshBufferLocations; - - std::map mSkinnedBuffers; - - struct - { - ComputeState::SharedPtr pState; - ComputeProgram::SharedPtr pProgram; - ComputeVars::SharedPtr pVars; - } mSkinningPass; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Paths/ObjectPath.cpp b/Framework/Source/Graphics/Paths/ObjectPath.cpp deleted file mode 100644 index ce721fc39..000000000 --- a/Framework/Source/Graphics/Paths/ObjectPath.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "Framework.h" -#include "ObjectPath.h" -#include "MovableObject.h" -#include - -namespace Falcor -{ - ObjectPath::SharedPtr ObjectPath::create() - { - return SharedPtr(new ObjectPath); - } - - ObjectPath::~ObjectPath() = default; - - uint32_t ObjectPath::addKeyFrame(float time, const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) - { - Frame keyFrame; - keyFrame.time = time; - keyFrame.target = target; - keyFrame.position = position; - keyFrame.up = up; - mDirty = true; - - if(mKeyFrames.size() == 0 || mKeyFrames[0].time > time) - { - mKeyFrames.insert(mKeyFrames.begin(), keyFrame); - return 0; - } - else - { - for(size_t i = 0 ; i < mKeyFrames.size() ; i++) - { - auto& current = mKeyFrames[i]; - // If we already have a key-frame at the same time, replace it - if(current.time == time) - { - current = keyFrame; - return (uint32_t)i; - } - - // If this is not the last frame, Check if we are in between frames - if(i < mKeyFrames.size() - 1) - { - auto& Next = mKeyFrames[i + 1]; - if(current.time < time && Next.time > time) - { - mKeyFrames.insert(mKeyFrames.begin() + i + 1, keyFrame); - return (uint32_t)i + 1; - } - } - } - - // If we got here, need to push it to the end of the list - mKeyFrames.push_back(keyFrame); - return (uint32_t)mKeyFrames.size() - 1; - } - } - - bool ObjectPath::animate(double currentTime) - { - if(mKeyFrames.size() == 0 || mpObjects.size() == 0) - { - return false; - } - - double animTime = currentTime; - const auto& firstFrame = mKeyFrames[0]; - const auto& lastFrame = mKeyFrames[mKeyFrames.size() - 1]; - if(mRepeatAnimation) - { - float delta = lastFrame.time - firstFrame.time; - if(delta) - { - animTime = float(fmod(currentTime, delta)); - animTime += firstFrame.time; - } - else - animTime = lastFrame.time; - } - - if(animTime >= lastFrame.time) - { - mCurrentFrame = lastFrame; - } - else if(animTime <= firstFrame.time) - { - mCurrentFrame = firstFrame; - } - else - { - // Find out where we are - bool foundFrame = false; - for(uint32_t i = 0 ; i < (uint32_t)mKeyFrames.size() - 1; i++) - { - const auto& curKey = mKeyFrames[i]; - const auto& nextKey = mKeyFrames[i + 1]; - - if(animTime >= curKey.time && animTime < nextKey.time) - { - // Found the animation keys. Interpolate - float t = getInterpolationFactor(i, animTime); - getFrameAt(i, t, mCurrentFrame); - foundFrame = true; - break; - } - } - assert(foundFrame); - } - - for(auto& pObj : mpObjects) - { - pObj->move(mCurrentFrame.position, mCurrentFrame.target, mCurrentFrame.up); - } - - return true; - } - - void ObjectPath::getFrameAt(uint32_t frameID, float t, Frame& frameOut) - { - if (getKeyFrameCount() == 1) - { - frameOut = mKeyFrames[0]; - return; - } - - if (mMode == Interpolation::Linear || getKeyFrameCount() < 3) - { - frameOut = linearInterpolation(frameID, t); - return; - } - else if(mMode == Interpolation::CubicSpline) - { - frameOut = cubicSplineInterpolation(frameID, t); - return; - } - - should_not_get_here(); - return; - } - - float ObjectPath::getInterpolationFactor(uint32_t frameID, double currentTime) const - { - const Frame& current = mKeyFrames[frameID]; - const Frame& next = mKeyFrames[frameID + 1]; - double delta = next.time - current.time; - double curTime = currentTime - current.time; - return float(curTime / delta); - } - - ObjectPath::Frame ObjectPath::linearInterpolation(uint32_t currentFrame, float t) const - { - const uint32_t nextFrame = std::min(currentFrame + 1, getKeyFrameCount() - 1); - - const Frame& current = mKeyFrames[currentFrame]; - const Frame& next = mKeyFrames[nextFrame]; - - Frame result; - result.position = glm::mix(current.position, next.position, t); - result.target = glm::mix(current.target, next.target, t); - result.up = glm::mix(current.up, next.up, t); - result.time = glm::mix(current.time, next.time, t); - - return result; - } - - ObjectPath::Frame ObjectPath::cubicSplineInterpolation(uint32_t currentFrame, float t) - { - if (mDirty) - { - mDirty = false; - std::vector positions, targets, ups; - for (auto& a : mKeyFrames) - { - positions.push_back(a.position); - targets.push_back(a.target); - ups.push_back(a.up); - } - - mpPositionSpline = std::make_unique(positions.data(), uint32_t(mKeyFrames.size())); - mpTargetSpline = std::make_unique(targets.data(), uint32_t(mKeyFrames.size())); - mpUpSpline = std::make_unique(ups.data(), uint32_t(mKeyFrames.size())); - } - - const Frame& current = mKeyFrames[currentFrame]; - const Frame& next = mKeyFrames[currentFrame + 1]; - - Frame result; - result.position = mpPositionSpline->interpolate(currentFrame, t); - result.target = mpTargetSpline->interpolate(currentFrame, t); - result.up = mpUpSpline->interpolate(currentFrame, t); - result.time = glm::mix(current.time, next.time, t); - - return result; - } - - void ObjectPath::attachObject(const IMovableObject::SharedPtr& pObject) - { - // Only attach the object if its not already found - if(std::find(mpObjects.begin(), mpObjects.end(), pObject) == mpObjects.end()) - { - mpObjects.push_back(pObject); - pObject->attachPath(this); - } - } - - void ObjectPath::detachObject(const IMovableObject::SharedPtr& pObject) - { - auto it = std::find(mpObjects.begin(), mpObjects.end(), pObject); - if(it != mpObjects.end()) - { - (*it)->attachPath(nullptr); - mpObjects.erase(it); - } - } - - void ObjectPath::detachAllObjects() - { - for (auto& pObj : mpObjects) - { - pObj->attachPath(nullptr); - } - mpObjects.clear(); - } - - void ObjectPath::removeKeyFrame(uint32_t frameID) - { - mKeyFrames.erase(mKeyFrames.begin() + frameID); - mDirty = true; - } - - uint32_t ObjectPath::setFrameTime(uint32_t frameID, float time) - { - const auto Frame = mKeyFrames[frameID]; - removeKeyFrame(frameID); - return addKeyFrame(time, Frame.position, Frame.target, Frame.up); - } - -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Paths/ObjectPath.h b/Framework/Source/Graphics/Paths/ObjectPath.h deleted file mode 100644 index f31a2349a..000000000 --- a/Framework/Source/Graphics/Paths/ObjectPath.h +++ /dev/null @@ -1,201 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "glm/vec3.hpp" -#include -#include "Graphics/Paths/MovableObject.h" -#include "Utils/Math/CubicSpline.h" - -namespace Falcor -{ - using Vec3CubicSpline = CubicSpline ; - - /** Describes and manages a path consisting of some number of key frames. Objects in a scene, such as models, lights, and cameras, - can be attached to a path and multiple objects can be attached to each path. Updating the path through animate() will update the - positions of all attached objects. - */ - class ObjectPath : public std::enable_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - /** Create a path. - */ - static ObjectPath::SharedPtr create(); - ~ObjectPath(); - - /** Ways to interpolate between key frames - */ - enum class Interpolation - { - Linear, ///< Linear interpolation. Requires at least 2 key frames - CubicSpline ///< Cubic spline interpolation. Requires at least 3 key frames. Path updates will fall-back to linear interpolation when there are less than 3 key frames - }; - - /** Set the interpolation mode. - */ - void setInterpolationMode(Interpolation mode) { mMode = mode; } - - /** Insert a key frame. Key frame will be inserted/sorted into the path based on time. - \param[in] time Time in seconds - \param[in] position World-space position - \param[in] target Look-at target position - \param[in] up Up vector - */ - uint32_t addKeyFrame(float time, const glm::vec3& position, const glm::vec3& target, const glm::vec3& up); - - /** Remove a key frame. Does not check bounds. - */ - void removeKeyFrame(uint32_t frameID); - - /** Update the current frame and updates the transforms on all attached objects. - \param[in] currentTime Elapsed time in seconds - \return Whether update was successful. - */ - bool animate(double currentTime); - - /** Attach a movable object to the path, such as models, cameras, and lights. - */ - void attachObject(const IMovableObject::SharedPtr& pObject); - - /** Detach a movable object from the path. - */ - void detachObject(const IMovableObject::SharedPtr& pObject); - - /** Detach all objects from the path. - */ - void detachAllObjects(); - - /** Get an object attached to the path. - */ - const IMovableObject::SharedPtr& getAttachedObject(uint32_t i) const { return mpObjects[i]; } - - /** Get the number of attached objects. - */ - uint32_t getAttachedObjectCount() const { return (uint32_t)mpObjects.size(); } - - /** Set whether the animation should loop upon reaching the last key frame. - */ - void setAnimationRepeat(bool repeatAnimation) { mRepeatAnimation = repeatAnimation; } - - /** Get the current interpolated position along the path. - */ - const glm::vec3& getCurrentPosition() const { return mCurrentFrame.position; } - - /** Get the current interpolated look-at target position (NOT vector) along the path. - */ - const glm::vec3& getCurrentLookAtVector() const { return mCurrentFrame.target; } - - /** Get the current interpolated up vector along the path. - */ - const glm::vec3& getCurrentUpVector() const { return mCurrentFrame.up; } - - /** Get whether the animation will loop. - */ - bool isRepeatOn() const {return mRepeatAnimation;} - - /** Get the path name. - */ - const std::string& getName() const { return mName; } - - /** Set the path name. - */ - void setName(const std::string& name) { mName = name; } - - /** Frame data - */ - struct Frame - { - glm::vec3 position; - glm::vec3 target; - glm::vec3 up; - float time = 0; - }; - - /** Get the number of key frames in the path. - */ - uint32_t getKeyFrameCount() const {return (uint32_t)mKeyFrames.size();} - - /** Get a particular keyframe. - */ - const Frame& getKeyFrame(uint32_t frameID) const { return mKeyFrames[frameID]; } - - /** Set a key frame's position. - \param[in] frameID Key frame index - \param[in] pos Position - */ - void setFramePosition(uint32_t frameID, const glm::vec3& pos) { mDirty = true; mKeyFrames[frameID].position = pos; } - - /** Set a key frame's look-at target. - \param[in] frameID Key frame index - \param[in] target Target position - */ - void setFrameTarget(uint32_t frameID, const glm::vec3& target) { mDirty = true; mKeyFrames[frameID].target = target; } - - /** Set a key frame's up vector. - \param[in] frameID Key frame index - \param[in] up Up vector - */ - void setFrameUp(uint32_t frameID, const glm::vec3& up) { mDirty = true; mKeyFrames[frameID].up = up; } - - /** Set a key frame's time. This will re-sort the key frame in the path. - \param[in] frameID Key frame index - \param[in] time Time in seconds - */ - uint32_t setFrameTime(uint32_t frameID, float time); - - /** Get interpolated frame data without modifying the path's current state. - \param[in] frameID Beginning frame ID - \param[in] t Interpolation factor between 0 and 1 (between frameID, and the next frame). Respects the path's interpolation mode. - \param[out] frameOut Frame data struct to store output - */ - void getFrameAt(uint32_t frameID, float t, Frame& frameOut); - - private: - ObjectPath() = default; - - float getInterpolationFactor(uint32_t frameID, double currentTime) const; - - Frame linearInterpolation(uint32_t currentFrame, float t) const; - Frame cubicSplineInterpolation(uint32_t currentFrame, float t); - - std::vector mKeyFrames; - std::vector mpObjects; - std::string mName; - bool mRepeatAnimation = false; - - Frame mCurrentFrame; - Interpolation mMode = Interpolation::CubicSpline; - bool mDirty = false; - - std::unique_ptr mpPositionSpline; - std::unique_ptr mpTargetSpline; - std::unique_ptr mpUpSpline; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Paths/PathEditor.cpp b/Framework/Source/Graphics/Paths/PathEditor.cpp deleted file mode 100644 index eba00055d..000000000 --- a/Framework/Source/Graphics/Paths/PathEditor.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "ObjectPath.h" -#include "PathEditor.h" -#include "Graphics/Camera/Camera.h" -#include "Utils/StringUtils.h" -#include - -namespace Falcor -{ - bool PathEditor::closeEditor(Gui* pGui) - { - if (pGui->addButton("Close Editor")) - { - pGui->popWindow(); - if (mEditCompleteCB) - { - mEditCompleteCB(); - } - return true; - } - return false; - } - - void PathEditor::editKeyframeProperties(Gui* pGui) - { - if (mpPath->getKeyFrameCount() > 0) - { - const auto& keyframe = mpPath->getKeyFrame(mActiveFrame); - - vec3 p = keyframe.position; - vec3 t = keyframe.target; - vec3 u = keyframe.up; - - bool changed = false; - - if (pGui->addFloat3Var("Keyframe Position", p, -FLT_MAX, FLT_MAX)) - { - mpPath->setFramePosition(mActiveFrame, p); - changed = true; - } - - if (pGui->addFloat3Var("Keyframe Target", t, -FLT_MAX, FLT_MAX)) - { - mpPath->setFrameTarget(mActiveFrame, t); - changed = true; - } - - if (pGui->addFloat3Var("Keyframe Up", u, -FLT_MAX, FLT_MAX)) - { - mpPath->setFrameUp(mActiveFrame, u); - changed = true; - } - - if (changed) - { - mFrameChangedCB(); - } - } - } - - void PathEditor::editActiveFrameID(Gui* pGui) - { - if(mpPath->getKeyFrameCount() > 0) - { - if (pGui->addIntVar("Selected Frame", mActiveFrame, 0, mpPath->getKeyFrameCount() - 1)) - { - setActiveFrame(mActiveFrame); - } - } - } - - void PathEditor::setActiveFrame(uint32_t id) - { - mActiveFrame = id; - mFrameTime = mpPath->getKeyFrame(mActiveFrame).time; - mFrameChangedCB(); - } - - void PathEditor::editPathLoop(Gui* pGui) - { - bool loop = mpPath->isRepeatOn(); - if (pGui->addCheckBox("Loop Path", loop)) - { - mpPath->setAnimationRepeat(loop); - } - } - - void PathEditor::editPathName(Gui* pGui) - { - char buffer[1024]; - copyStringToBuffer(buffer, arraysize(buffer), mpPath->getName()); - - if (pGui->addTextBox("Path Name", buffer, arraysize(buffer))) - { - mpPath->setName(buffer); - } - } - - PathEditor::UniquePtr PathEditor::create(const ObjectPath::SharedPtr& pPath, const Camera::SharedPtr& pCamera, PathEditorCallback frameChangedCB, PathEditorCallback addRemoveKeyframeCB, PathEditorCallback editCompleteCB) - { - return UniquePtr(new PathEditor(pPath, pCamera, frameChangedCB, addRemoveKeyframeCB, editCompleteCB)); - } - - PathEditor::PathEditor(const ObjectPath::SharedPtr& pPath, const Camera::SharedPtr& pCamera, PathEditorCallback frameChangedCB, PathEditorCallback addRemoveKeyframeCB, PathEditorCallback editCompleteCB) - : mpPath(pPath) - , mpCamera(pCamera) - , mFrameChangedCB(frameChangedCB) - , mAddRemoveKeyframeCB(addRemoveKeyframeCB) - , mEditCompleteCB(editCompleteCB) - { - if(mpPath->getKeyFrameCount() > 0) - { - mFrameTime = mpPath->getKeyFrame(0).time; - } - } - - PathEditor::~PathEditor() - { - } - - void PathEditor::render(Gui* pGui) - { - pGui->pushWindow("Path Editor", 350, 400, 440, 400); - if (closeEditor(pGui)) return; - pGui->addSeparator(); - editPathName(pGui); - editPathLoop(pGui); - editActiveFrameID(pGui); - - addFrame(pGui); - deleteFrame(pGui); - pGui->addSeparator(); - editFrameTime(pGui); - updateFrameTime(pGui); - - pGui->addSeparator(); - editKeyframeProperties(pGui); - moveToCamera(pGui); - pGui->popWindow(); - } - - void PathEditor::editFrameTime(Gui* pGui) - { - pGui->addFloatVar("Frame Time", mFrameTime, 0, FLT_MAX); - } - - void PathEditor::addFrame(Gui* pGui) - { - if(pGui->addButton("Add Frame")) - { - // If path has keyframes, create new keyframe at the location of the selected keyframe - if (mpPath->getKeyFrameCount() > 0) - { - const auto& currFrame = mpPath->getKeyFrame(mActiveFrame); - mActiveFrame = mpPath->addKeyFrame(mFrameTime, currFrame.position, currFrame.target, currFrame.up); - } - else - { - mActiveFrame = mpPath->addKeyFrame(mFrameTime, glm::vec3(), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - } - - mAddRemoveKeyframeCB(); - - setActiveFrame(mActiveFrame); - } - } - - void PathEditor::deleteFrame(Gui* pGui) - { - if (mpPath->getKeyFrameCount() > 0) - { - if(pGui->addButton("Remove Frame", true)) - { - mpPath->removeKeyFrame(mActiveFrame); - mAddRemoveKeyframeCB(); - - mActiveFrame = min(mpPath->getKeyFrameCount() - 1, (uint32_t)mActiveFrame); - - if (mpPath->getKeyFrameCount() > 0) - { - setActiveFrame(mActiveFrame); - } - } - } - } - - void PathEditor::updateFrameTime(Gui* pGui) - { - if (mpPath->getKeyFrameCount() && pGui->addButton("Update Current Frame Time")) - { - mActiveFrame = mpPath->setFrameTime(mActiveFrame, mFrameTime); - mAddRemoveKeyframeCB(); - - setActiveFrame(mActiveFrame); - } - } - - void PathEditor::moveToCamera(Gui* pGui) - { - if (mpPath->getKeyFrameCount() > 0) - { - if (pGui->addButton("Move Frame to Camera")) - { - mpPath->setFramePosition(mActiveFrame, mpCamera->getPosition()); - mpPath->setFrameTarget(mActiveFrame, mpCamera->getTarget()); - mpPath->setFrameUp(mActiveFrame, mpCamera->getUpVector()); - mFrameChangedCB(); - } - } - } - -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Paths/PathEditor.h b/Framework/Source/Graphics/Paths/PathEditor.h deleted file mode 100644 index 4b5829445..000000000 --- a/Framework/Source/Graphics/Paths/PathEditor.h +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Utils/Gui.h" -#include "Graphics/Paths/ObjectPath.h" -#include "Graphics/Camera/Camera.h" -#include "Graphics/Scene/Scene.h" - -namespace Falcor -{ - class PathEditor - { - public: - using PathEditorCallback = std::function; - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - /** Create a path editor instance - \param[in] pPath Path to edit - \param[in] pCamera Camera being used in the scene. Allows user to update key frame orientation based on a camera - \param[in] frameChangedCB Function that is called when a key frame's data has been updated - \param[in] addRemoveKeyframeCB Function that is called when a key frame has been added or removed - \param[in] editCompleteCB Function that is called when the path editor is closed - */ - static UniquePtr create(const ObjectPath::SharedPtr& pPath, const Camera::SharedPtr& pCamera, PathEditorCallback frameChangedCB, PathEditorCallback addRemoveKeyframeCB, PathEditorCallback editCompleteCB); - ~PathEditor(); - - /** Render the editor UI elements. - \param[in] pGui GUI object to render the editor UI with - */ - void render(Gui* pGui); - - /** Set the active key frame to edit. - \param[in] id Key frame id - */ - void setActiveFrame(uint32_t id); - - /** Get the active key frame - */ - uint32_t getActiveFrame() const { return mActiveFrame; } - - /** Get the path being edited. - */ - const ObjectPath::SharedPtr& getPath() const { return mpPath; } - - private: - PathEditor(const ObjectPath::SharedPtr& pPath, const Camera::SharedPtr& pCamera, PathEditorCallback frameChangedCB, PathEditorCallback addRemoveKeyframeCB, PathEditorCallback editCompleteCB); - - bool closeEditor(Gui* pGui); - void editPathName(Gui* pGui); - void editPathLoop(Gui* pGui); - void editActiveFrameID(Gui* pGui); - void addFrame(Gui* pGui); - void deleteFrame(Gui* pGui); - void editFrameTime(Gui* pGui); - void editKeyframeProperties(Gui* pGui); - - void updateFrameTime(Gui* pGui); - void moveToCamera(Gui* pGui); - - ObjectPath::SharedPtr mpPath; - Camera::SharedPtr mpCamera; - - PathEditorCallback mEditCompleteCB; - PathEditorCallback mFrameChangedCB; - PathEditorCallback mAddRemoveKeyframeCB; - - int32_t mActiveFrame = 0; - float mFrameTime = 0; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Scene/Editor/SceneEditor.cpp b/Framework/Source/Graphics/Scene/Editor/SceneEditor.cpp deleted file mode 100644 index 536a6ccbc..000000000 --- a/Framework/Source/Graphics/Scene/Editor/SceneEditor.cpp +++ /dev/null @@ -1,1584 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "Framework.h" -#include "Graphics/Scene/Scene.h" -#include "Graphics/Scene/Editor/SceneEditor.h" -#include "Utils/Gui.h" -#include "Utils/Platform/OS.h" -#include "Graphics/Scene/SceneExporter.h" -#include "Graphics/Model/AnimationController.h" -#include "API/Device.h" -#include "Graphics/Model/ModelRenderer.h" -#include "Utils/Math/FalcorMath.h" -#include "Data/HostDeviceData.h" -#include "Utils/StringUtils.h" - -namespace Falcor -{ - namespace - { - const char* kSelectedModelStr = "Selected Model"; - const char* kModelsStr = "Models"; - const char* kSelectedInstanceStr = "Selected Instance"; - const char* kActiveAnimationStr = "Active Animation"; - const char* kModelNameStr = "Model Name"; - const char* kShadingModelStr = "Shading Model"; - const char* kInstanceStr = "Instance"; - const char* kCamerasStr = "Cameras"; - const char* kActiveCameraStr = "Active Camera"; - const char* kPathsStr = "Paths"; - const char* kSelectedPathStr = "Selected Path"; - }; - - const float SceneEditor::kCameraModelScale = 0.5f; - const float SceneEditor::kLightModelScale = 0.3f; - const float SceneEditor::kKeyframeModelScale = 0.2f; - - const Gui::DropdownList SceneEditor::kShadingModelList = - { - { ShadingModelMetalRough, "Metal-Rough" }, - { ShadingModelSpecGloss, "Spec-Gloss" } - }; - - const Gui::RadioButtonGroup SceneEditor::kGizmoSelectionButtons - { - { (uint32_t)Gizmo::Type::Translate, "Translation", false }, - { (uint32_t)Gizmo::Type::Rotate, "Rotation", true }, - { (uint32_t)Gizmo::Type::Scale, "Scaling", true } - }; - - Gui::DropdownList getPathDropdownList(const Scene* pScene, bool includeDefault) - { - Gui::DropdownList pathList; - static const Gui::DropdownValue kNoPathValue{ Scene::kNoPath, "None" }; - - if (includeDefault) - { - pathList.push_back(kNoPathValue); - } - - for (uint32_t i = 0; i < pScene->getPathCount(); i++) - { - Gui::DropdownValue value; - value.label = pScene->getPath(i)->getName(); - value.value = i; - pathList.push_back(value); - } - - return pathList; - } - - // - // SceneEditor - // - - void SceneEditor::selectActiveModel(Gui* pGui) - { - Gui::DropdownList modelList; - for (uint32_t i = 0; i < mpScene->getModelCount(); i++) - { - Gui::DropdownValue value; - value.label = mpScene->getModel(i)->getName(); - value.value = i; - modelList.push_back(value); - } - - if (pGui->addDropdown(kSelectedModelStr, modelList, mSelectedModel)) - { - mSelectedModelInstance = 0; - } - } - - void SceneEditor::setModelName(Gui* pGui) - { - char modelName[1024]; - copyStringToBuffer(modelName, arraysize(modelName), mpScene->getModel(mSelectedModel)->getName()); - if (pGui->addTextBox(kModelNameStr, modelName, arraysize(modelName))) - { - mpScene->getModel(mSelectedModel)->setName(modelName); - mSceneDirty = true; - } - } - - void SceneEditor::setShadingModel(Gui* pGui) - { - auto& pModel = mpScene->getModel(mSelectedModel); - assert(pModel->getMeshCount() > 0); - assert(pModel->getMesh(0)->getMaterial() != nullptr); - - uint32_t shadingModel = pModel->getMesh(0)->getMaterial()->getShadingModel(); - if (pGui->addDropdown(kShadingModelStr, kShadingModelList, shadingModel)) - { - for (uint32_t i = 0; i < pModel->getMeshCount(); i++) - { - pModel->getMesh(i)->getMaterial()->setShadingModel(shadingModel); - } - mSceneDirty = true; - } - } - - void SceneEditor::setModelVisible(Gui* pGui) - { - const Scene::ModelInstance::SharedPtr& instance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - bool visible = instance->isVisible(); - if (pGui->addCheckBox("Visible", visible)) - { - instance->setVisible(visible); - mSceneDirty = true; - } - } - - void SceneEditor::selectPath(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - uint32_t activePath = mSelectedPath; - Gui::DropdownList pathList = getPathDropdownList(mpScene.get(), false); - - if (pathList.size() > 0) - { - if (pGui->addDropdown(kSelectedPathStr, pathList, activePath)) - { - mSelectedPath = activePath; - mSceneDirty = true; - } - } - } - else - { - std::string msg = kSelectedPathStr + std::string(": ") + mpScene->getPath(mSelectedPath)->getName(); - pGui->addText(msg.c_str()); - } - } - - void SceneEditor::setActiveCamera(Gui* pGui) - { - Gui::DropdownList cameraList; - for (uint32_t i = 0; i < mpScene->getCameraCount(); i++) - { - Gui::DropdownValue value; - value.label = mpScene->getCamera(i)->getName(); - value.value = i; - cameraList.push_back(value); - } - - uint32_t camIndex = mpScene->getActiveCameraIndex(); - if (pGui->addDropdown(kActiveCameraStr, cameraList, camIndex)) - { - mpScene->setActiveCamera(camIndex); - mSceneDirty = true; - } - } - - void SceneEditor::setCameraName(Gui* pGui) - { - char camName[1024]; - std::string oldName = mpScene->getActiveCamera()->getName(); - copyStringToBuffer(camName, arraysize(camName), oldName); - if (pGui->addTextBox("Camera Name", camName, arraysize(camName))) - { - std::string newName(camName); - - if (mCameraNames.count(newName) > 0) - { - msgBox("Another camera already exists with that name!"); - return; - } - - mpScene->getActiveCamera()->setName(camName); - - mCameraNames.erase(oldName); - mCameraNames.emplace(newName); - - mSceneDirty = true; - } - } - - void SceneEditor::setCameraSpeed(Gui* pGui) - { - float speed = mpScene->getCameraSpeed(); - if (pGui->addFloatVar("Camera Speed", speed, 0, FLT_MAX, 0.1f)) - { - mpScene->setCameraSpeed(speed); - mSceneDirty = true; - } - } - - void SceneEditor::setInstanceTranslation(Gui* pGui) - { - auto& pInstance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - vec3 t = pInstance->getTranslation(); - if (pGui->addFloat3Var("Translation", t, -FLT_MAX, FLT_MAX)) - { - pInstance->setTranslation(t, true); - mSceneDirty = true; - } - } - - void SceneEditor::setInstanceRotation(Gui* pGui) - { - vec3 r = getActiveInstanceRotationAngles(); - r = degrees(r); - if (pGui->addFloat3Var("Rotation", r, -360, 360)) - { - r = radians(r); - setActiveInstanceRotationAngles(r); - mSceneDirty = true; - } - } - - void SceneEditor::setInstanceScaling(Gui* pGui) - { - auto& pInstance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - vec3 s = pInstance->getScaling(); - if (pGui->addFloat3Var("Scaling", s, 0, FLT_MAX)) - { - pInstance->setScaling(s); - mSceneDirty = true; - } - } - - void SceneEditor::deleteLight(uint32_t id) - { - detachObjectFromPaths(mpScene->getLight(id)); - mLightNames.erase(mpScene->getLight(id)->getName()); - - if (mpScene->getLight(id)->getType() == LightPoint) - { - uint32_t instanceID = mLightIDSceneToEditor.at(id); - mpEditorScene->deleteModelInstance(mEditorLightModelID, instanceID); - } - - mpScene->deleteLight(id); - - updateEditorModelIDs(); - rebuildLightIDMap(); - - deselect(); - } - - void SceneEditor::addPointLight(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Add Point Light")) - { - if (mpScene->getLightCount() >= MAX_LIGHT_SOURCES) - { - msgBox("There cannot be more than 16 lights at a time in a scene!"); - return; - } - - auto pNewLight = PointLight::create(); - - // Place in front of camera - const auto& pCamera = mpEditorScene->getActiveCamera(); - glm::vec3 forward = glm::normalize(pCamera->getTarget() - pCamera->getPosition()); - pNewLight->setWorldPosition(pCamera->getPosition() + forward); - - mSelectedLight = mpScene->addLight(pNewLight); - - // Name - std::string name = getUniqueNumberedName("PointLight", 0, mLightNames); - pNewLight->setName(name); - mLightNames.insert(name); - - mpEditorScene->addModelInstance(mpLightModel, name, glm::vec3(), glm::vec3(), glm::vec3(kLightModelScale)); - updateEditorModelIDs(); - rebuildLightIDMap(); - - select(mpEditorScene->getModelInstance(mEditorLightModelID, mLightIDSceneToEditor[mSelectedLight])); - - mSceneDirty = true; - } - } - } - - void SceneEditor::addDirectionalLight(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Add Directional Light")) - { - if (mpScene->getLightCount() >= MAX_LIGHT_SOURCES) - { - msgBox("There cannot be more than 16 lights at a time in a scene!"); - return; - } - - auto pNewLight = DirectionalLight::create(); - mpScene->addLight(pNewLight); - - // Name - std::string name = getUniqueNumberedName("DirLight", 0, mLightNames); - pNewLight->setName(name); - mLightNames.insert(name); - - mSceneDirty = true; - } - } - } - - void SceneEditor::saveScene() - { - std::string filename; - if (saveFileDialog(Scene::kFileExtensionFilters, filename)) - { - SceneExporter::saveScene(filename, mpScene); - mSceneDirty = false; - } - } - - ////////////////////////////////////////////////////////////////////////// - //// End callbacks - ////////////////////////////////////////////////////////////////////////// - - SceneEditor::UniquePtr SceneEditor::create(const Scene::SharedPtr& pScene, Model::LoadFlags modelLoadFlags) - { - return UniquePtr(new SceneEditor(pScene, modelLoadFlags)); - } - - SceneEditor::SceneEditor(const Scene::SharedPtr& pScene, Model::LoadFlags modelLoadFlags) - : mpScene(pScene) - , mModelLoadFlags(modelLoadFlags) - { - mpDebugDrawer = DebugDrawer::create(); - - initializeEditorRendering(); - initializeEditorObjects(); - - // Copy camera transform from master scene - const auto& pSceneCamera = mpScene->getActiveCamera(); - - if (pSceneCamera) - { - const auto& pEditorCamera = mpEditorScene->getActiveCamera(); - - pEditorCamera->setPosition(pSceneCamera->getPosition()); - pEditorCamera->setUpVector(pSceneCamera->getUpVector()); - pEditorCamera->setTarget(pSceneCamera->getTarget()); - } - } - - SceneEditor::~SceneEditor() - { - if (mSceneDirty && mpScene) - { - if (msgBox("Scene changed. Do you want to save the changes?", MsgBoxType::OkCancel) == MsgBoxButton::Ok) - { - saveScene(); - } - } - } - - void SceneEditor::update(double currentTime) - { - mpEditorSceneRenderer->update(currentTime); - } - - void SceneEditor::initializeEditorRendering() - { - auto backBufferFBO = gpDevice->getSwapChainFbo(); - uint32_t backBufferWidth = backBufferFBO->getWidth(); - uint32_t backBufferHeight = backBufferFBO->getHeight(); - - // - // Selection Wireframe Scene - // - - mpSelectionGraphicsState = GraphicsState::create(); - - // Rasterizer State for rendering wireframe of selected object - RasterizerState::Desc wireFrameRSDesc; - wireFrameRSDesc.setFillMode(RasterizerState::FillMode::Wireframe).setCullMode(RasterizerState::CullMode::None).setDepthBias(-5, 0.0f); - mpSelectionGraphicsState->setRasterizerState(RasterizerState::create(wireFrameRSDesc)); - - // Depth test - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(true); - DepthStencilState::SharedPtr depthTestDS = DepthStencilState::create(dsDesc); - mpSelectionGraphicsState->setDepthStencilState(depthTestDS); - - // Shader - mpColorProgram = GraphicsProgram::createFromFile("Framework/Shaders/SceneEditor.slang", "editorVs", "editorPs"); - mpColorProgramVars = GraphicsVars::create(mpColorProgram->getReflector()); - mpSelectionGraphicsState->setProgram(mpColorProgram); - - // Selection Scene and Renderer - mpSelectionScene = Scene::create(); - mpSelectionSceneRenderer = SceneRenderer::create(mpSelectionScene); - - // - // Master Scene Picking - // - - mpScenePicker = Picking::create(mpScene, backBufferWidth, backBufferHeight); - - // - // Editor Scene and Picking - // - - mpEditorScene = Scene::create(); - mpEditorScene->addCamera(Camera::create()); - mpEditorScene->getActiveCamera()->setAspectRatio((float)backBufferWidth / (float)backBufferHeight); - mpEditorSceneRenderer = SceneEditorRenderer::create(mpEditorScene); - mpEditorPicker = Picking::create(mpEditorScene, backBufferWidth, backBufferHeight); - - // - // Debug Draw Shaders - // - - RasterizerState::Desc lineRSDesc; - lineRSDesc.setFillMode(RasterizerState::FillMode::Solid).setCullMode(RasterizerState::CullMode::None); - - mpDebugDrawProgram = GraphicsProgram::createFromFile("Framework/Shaders/SceneEditor.slang", "debugDrawVs", "debugDrawPs"); - mpDebugDrawProgramVars = GraphicsVars::create(mpDebugDrawProgram->getReflector()); - - mpPathGraphicsState = GraphicsState::create(); - mpPathGraphicsState->setProgram(mpDebugDrawProgram); - mpPathGraphicsState->setDepthStencilState(depthTestDS); - mpPathGraphicsState->setRasterizerState(RasterizerState::create(lineRSDesc)); - } - - void SceneEditor::initializeEditorObjects() - { - // - // Gizmos - // - - mGizmos[(uint32_t)Gizmo::Type::Translate] = TranslateGizmo::create(mpEditorScene, "Framework/Models/TranslateGizmo.obj"); - mGizmos[(uint32_t)Gizmo::Type::Rotate] = RotateGizmo::create(mpEditorScene, "Framework/Models/RotateGizmo.obj"); - mGizmos[(uint32_t)Gizmo::Type::Scale] = ScaleGizmo::create(mpEditorScene, "Framework/Models/ScaleGizmo.obj"); - - mpEditorSceneRenderer->registerGizmos(mGizmos); - mpEditorPicker->registerGizmos(mGizmos); - - // - // Cameras - // - - mpCameraModel = Model::createFromFile("Framework/Models/Camera.obj"); - - if (mpScene->getCameraCount() > 0) - { - for (uint32_t i = 0; i < mpScene->getCameraCount(); i++) - { - const auto& pCamera = mpScene->getCamera(i); - mpEditorScene->addModelInstance(mpCameraModel, "Camera " + std::to_string(i), glm::vec3(), glm::vec3(), glm::vec3(kCameraModelScale)); - - // Track camera names - mCameraNames.emplace(pCamera->getName()); - } - } - - // - // Lights - // - - mpLightModel = Model::createFromFile("Framework/Models/LightBulb.obj"); - - uint32_t pointLightID = 0; - for (uint32_t i = 0; i < mpScene->getLightCount(); i++) - { - const auto& pLight = mpScene->getLight(i); - if (pLight->getType() == LightPoint) - { - mpEditorScene->addModelInstance(mpLightModel, "Point Light " + std::to_string(pointLightID++), glm::vec3(), glm::vec3(), glm::vec3(kLightModelScale)); - } - - // Track light names - mLightNames.emplace(pLight->getName()); - } - - rebuildLightIDMap(); - updateEditorModelIDs(); - - // - // Master Scene Model Instance Rotations - // - - for (uint32_t modelID = 0; modelID < mpScene->getModelCount(); modelID++) - { - mInstanceRotationAngles.emplace_back(); - - for (uint32_t instanceID = 0; instanceID < mpScene->getModelInstanceCount(modelID); instanceID++) - { - auto& pInstance = mpScene->getModelInstance(modelID, instanceID); - mInstanceRotationAngles[modelID].push_back(pInstance->getRotation()); - - // Track model instance names - mInstanceNames.emplace(pInstance->getName()); - } - } - - // - // Path Attachments - // - - for (uint32_t pathID = 0; pathID < mpScene->getPathCount(); pathID++) - { - const auto& pPath = mpScene->getPath(pathID); - for (uint32_t i = 0; i < pPath->getAttachedObjectCount(); i++) - { - mObjToPathMap[pPath->getAttachedObject(i).get()] = pPath; - } - } - - mpKeyframeModel = Model::createFromFile("Framework/Models/Camera.obj"); - } - - const glm::vec3& SceneEditor::getActiveInstanceRotationAngles() - { - return mInstanceRotationAngles[mSelectedModel][mSelectedModelInstance]; - } - - void SceneEditor::setActiveInstanceRotationAngles(const glm::vec3& rotation) - { - mInstanceRotationAngles[mSelectedModel][mSelectedModelInstance] = rotation; - mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance)->setRotation(rotation); - mSceneDirty = true; - } - - void SceneEditor::render(RenderContext* pContext) - { - Camera *pCamera = mpEditorScene->getActiveCamera().get(); - - // Draw to same Fbo that was set before this call - mpSelectionGraphicsState->setFbo(pContext->getGraphicsState()->getFbo()); - - updateEditorObjectTransforms(); - - // Rendered selected model wireframe - if (mSelectedInstances.empty() == false && mHideWireframe == false) - { - pContext->setGraphicsState(mpSelectionGraphicsState); - mpColorProgramVars["ConstColorCB"]["gColor"] = glm::vec3(0.25f, 1.0f, 0.63f); - - pContext->setGraphicsVars(mpColorProgramVars); - mpSelectionSceneRenderer->renderScene(pContext, pCamera); - } - - // Camera/Light Models, and Gizmos - mpEditorSceneRenderer->renderScene(pContext, pCamera); - - // Paths - if (mpPathEditor != nullptr || mRenderAllPaths) - { - renderPath(pContext); - } - } - - void SceneEditor::updateEditorObjectTransforms() - { - // Update Gizmo model - if (mSelectedInstances.empty() == false) - { - const auto& activeInstance = mpSelectionScene->getModelInstance(0, 0); - mGizmos[(uint32_t)mActiveGizmoType]->setTransform(mpEditorScene->getActiveCamera(), activeInstance); - } - - // Update camera model transforms - for (uint32_t i = 0; i < mpScene->getCameraCount(); i++) - { - updateCameraModelTransform(i); - } - - // Update light model transforms if any exist - if (mEditorLightModelID != (uint32_t)-1) - { - for (uint32_t i = 0; i < mpEditorScene->getModelInstanceCount(mEditorLightModelID); i++) - { - const auto& pLight = mpScene->getLight(mLightIDEditorToScene[i]); - auto& pModelInstance = mpEditorScene->getModelInstance(mEditorLightModelID, i); - pModelInstance->setTranslation(pLight->getData().posW, true); - } - } - - // Update keyframe models if path editor is open - if (mpPathEditor != nullptr) - { - const auto& pPath = mpPathEditor->getPath(); - for (uint32_t i = 0; i < pPath->getKeyFrameCount(); i++) - { - auto& pInstance = mpEditorScene->getModelInstance(mEditorKeyframeModelID, i); - - // Make keyframe model bigger if selected - if (mpPathEditor->getActiveFrame() == i && mSelectedObjectType == ObjectType::Keyframe) - { - pInstance->setScaling(glm::vec3(kKeyframeModelScale * 2.0f)); - } - else - { - pInstance->setScaling(glm::vec3(kKeyframeModelScale)); - } - } - } - } - - void SceneEditor::updateCameraModelTransform(uint32_t cameraID) - { - const auto& pCamera = mpScene->getCamera(cameraID); - auto& pInstance = mpEditorScene->getModelInstance(mEditorCameraModelID, cameraID); - - pInstance->setTranslation(pCamera->getPosition(), false); - pInstance->setTarget(pCamera->getTarget()); - pInstance->setUpVector(pCamera->getUpVector()); - } - - void SceneEditor::renderPath(RenderContext* pContext) - { - mpDebugDrawer->clear(); - - mpDebugDrawer->setColor(glm::vec3(0.25f, 1.0f, 0.63f)); - - if (mRenderAllPaths) - { - for (uint32_t i = 0; i < mpScene->getPathCount(); i++) - { - mpDebugDrawer->addPath(mpScene->getPath(i)); - } - } - else if (mpPathEditor != nullptr) - { - mpDebugDrawer->addPath(mpPathEditor->getPath()); - } - - mpPathGraphicsState->setFbo(pContext->getGraphicsState()->getFbo()); - pContext->setGraphicsState(mpPathGraphicsState); - pContext->setGraphicsVars(mpDebugDrawProgramVars); - - mpDebugDrawer->render(pContext, mpEditorScene->getActiveCamera().get()); - } - - void SceneEditor::detachObjectFromPaths(const IMovableObject::SharedPtr& pMovable) - { - for (uint32_t i = 0; i < mpScene->getPathCount(); i++) - { - mpScene->getPath(i)->detachObject(pMovable); - } - } - - void SceneEditor::rebuildLightIDMap() - { - mLightIDEditorToScene.clear(); - mLightIDSceneToEditor.clear(); - - uint32_t pointLightID = 0; - for (uint32_t sceneLightID = 0; sceneLightID < mpScene->getLightCount(); sceneLightID++) - { - const auto& pLight = mpScene->getLight(sceneLightID); - - if (pLight->getType() == LightPoint) - { - mLightIDEditorToScene[pointLightID] = sceneLightID; - mLightIDSceneToEditor[sceneLightID] = pointLightID; - - pointLightID++; - } - } - } - - void SceneEditor::applyGizmoTransform() - { - const auto& activeGizmo = mGizmos[(uint32_t)mActiveGizmoType]; - - switch (mSelectedObjectType) - { - case ObjectType::Model: - { - auto& pInstance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - activeGizmo->applyDelta(pInstance); - - if (mActiveGizmoType == Gizmo::Type::Rotate) - { - mInstanceRotationAngles[mSelectedModel][mSelectedModelInstance] = pInstance->getRotation(); - } - break; - } - - case ObjectType::Camera: - activeGizmo->applyDelta(mpScene->getActiveCamera()); - break; - - case ObjectType::Light: - { - auto pPointLight = std::dynamic_pointer_cast(mpScene->getLight(mSelectedLight)); - if (pPointLight != nullptr) - { - activeGizmo->applyDelta(pPointLight); - mpEditorScene->getModelInstance(mEditorLightModelID, mLightIDSceneToEditor[mSelectedLight])->setTranslation(pPointLight->getWorldPosition(), true); - } - break; - } - - case ObjectType::Keyframe: - assert(mpPathEditor); - if (mActiveGizmoType != Gizmo::Type::Scale) - { - const uint32_t activeFrame = mpPathEditor->getActiveFrame(); - auto& pInstance = mpEditorScene->getModelInstance(mEditorKeyframeModelID, activeFrame); - activeGizmo->applyDelta(pInstance); - - auto& pPath = mpScene->getPath(mSelectedPath); - pPath->setFramePosition(activeFrame, pInstance->getTranslation()); - pPath->setFrameTarget(activeFrame, pInstance->getTarget()); - pPath->setFrameUp(activeFrame, pInstance->getUpVector()); - } - break; - } - - mSceneDirty = true; - } - - std::string SceneEditor::getUniqueNumberedName(const std::string& baseName, uint32_t idSuffix, const std::set& nameMap) const - { - std::string name; - do - { - name = baseName + std::to_string(idSuffix++); - } while (nameMap.count(name) > 0); - - return name; - } - - bool SceneEditor::onMouseEvent(RenderContext* pContext, const MouseEvent& mouseEvent) - { - // Update mouse hold timer - if (mouseEvent.type == MouseEvent::Type::LeftButtonDown || mouseEvent.type == MouseEvent::Type::LeftButtonUp) - { - mMouseHoldTimer.update(); - } - - // - // Scene Editor Mouse Handler - // - - switch (mouseEvent.type) - { - case MouseEvent::Type::LeftButtonDown: - // Gizmo Selection - if (mGizmoBeingDragged == false) - { - if (mpEditorPicker->pick(pContext, mouseEvent.pos, mpEditorScene->getActiveCamera())) - { - const auto& pInstance = mpEditorPicker->getPickedModelInstance(); - - // If picked model instance is part of the active gizmo - if (mGizmos[(uint32_t)mActiveGizmoType]->beginAction(mpEditorScene->getActiveCamera(), pInstance)) - { - mGizmoBeingDragged = true; - mGizmos[(uint32_t)mActiveGizmoType]->update(mpEditorScene->getActiveCamera(), mouseEvent); - } - } - } - break; - - case MouseEvent::Type::Move: - // Gizmo Drag - if (mGizmoBeingDragged) - { - mGizmos[(uint32_t)mActiveGizmoType]->update(mpEditorScene->getActiveCamera(), mouseEvent); - applyGizmoTransform(); - } - break; - - case MouseEvent::Type::LeftButtonUp: - if (mGizmoBeingDragged) - { - mGizmoBeingDragged = false; - } - else - { - // Scene Object Selection - if (mMouseHoldTimer.getElapsedTime() < 0.2f) - { - if (mpEditorPicker->pick(pContext, mouseEvent.pos, mpEditorScene->getActiveCamera())) - { - select(mpEditorPicker->getPickedModelInstance()); - } - else if (mpScenePicker->pick(pContext, mouseEvent.pos, mpEditorScene->getActiveCamera())) - { - select(mpScenePicker->getPickedModelInstance(), mpScenePicker->getPickedMeshInstance()); - } - else - { - deselect(); - } - } - } - break; - } - - // Update camera - if (mGizmoBeingDragged == false) - { - mpEditorSceneRenderer->onMouseEvent(mouseEvent); - } - - return true; - } - - bool SceneEditor::onKeyEvent(const KeyboardEvent& keyEvent) - { - return mpEditorSceneRenderer->onKeyEvent(keyEvent); - } - - void SceneEditor::onResizeSwapChain() - { - if (mpScenePicker) - { - auto backBufferFBO = gpDevice->getSwapChainFbo(); - mpScenePicker->resizeFBO(backBufferFBO->getWidth(), backBufferFBO->getHeight()); - } - } - - void SceneEditor::setActiveModelInstance(const Scene::ModelInstance::SharedConstPtr& pModelInstance) - { - for (uint32_t modelID = 0; modelID < mpScene->getModelCount(); modelID++) - { - // Model found, look for exact instance - if (mpScene->getModel(modelID) == pModelInstance->getObject()) - { - for (uint32_t instanceID = 0; instanceID < mpScene->getModelInstanceCount(modelID); instanceID++) - { - // Instance found - if (mpScene->getModelInstance(modelID, instanceID) == pModelInstance) - { - mSelectedModel = modelID; - mSelectedModelInstance = instanceID; - return; - } - } - - return; - } - } - } - - void SceneEditor::renderModelElements(Gui* pGui) - { - if (pGui->beginGroup(kModelsStr)) - { - addModel(pGui); - if (mpScene->getModelCount()) - { - deleteModel(pGui); - if (mpScene->getModelCount() == 0) - { - pGui->endGroup(); - return; - } - - pGui->addSeparator(); - selectActiveModel(pGui); - setModelName(pGui); - setShadingModel(pGui); - - if (pGui->beginGroup(kInstanceStr)) - { - addModelInstance(pGui); - addModelInstanceRange(pGui); - deleteModelInstance(pGui); - - if (mpScene->getModelCount() == 0) - { - pGui->endGroup(); - return; - } - - pGui->addSeparator(); - setModelVisible(pGui); - setInstanceTranslation(pGui); - setInstanceRotation(pGui); - setInstanceScaling(pGui); - setObjectPath(pGui, mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance), "ModelInstance"); - - pGui->endGroup(); - } - - renderModelAnimation(pGui); - } - pGui->endGroup(); - } - } - - void SceneEditor::renderGlobalElements(Gui* pGui) - { - if (pGui->beginGroup("Global Settings")) - { - setCameraSpeed(pGui); - pGui->endGroup(); - } - } - - void SceneEditor::renderPathElements(Gui* pGui) - { - if (pGui->beginGroup(kPathsStr)) - { - selectPath(pGui); - addPath(pGui); - startPathEditor(pGui); - deletePath(pGui); - pGui->addCheckBox("Render All Paths", mRenderAllPaths); - pGui->endGroup(); - } - } - - void SceneEditor::renderCameraElements(Gui* pGui) - { - if (pGui->beginGroup(kCamerasStr)) - { - addCamera(pGui); - if (mpScene->getCameraCount()) - { - setActiveCamera(pGui); - setCameraName(pGui); - deleteCamera(pGui); - - // Last camera could have just been deleted - if (mpScene->getCameraCount() > 0) - { - pGui->addSeparator(); - mpScene->getActiveCamera()->renderUI(pGui); - setObjectPath(pGui, mpScene->getActiveCamera(), "Camera"); - } - } - - pGui->endGroup(); - } - } - - void SceneEditor::renderLightElements(Gui* pGui) - { - if (pGui->beginGroup("Lights")) - { - addPointLight(pGui); - addDirectionalLight(pGui); - - for (uint32_t i = 0; i < mpScene->getLightCount(); i++) - { - std::string name = mpScene->getLight(i)->getName(); - if (name.size() == 0) - { - name = std::string("Light ") + std::to_string(i); - } - if (pGui->beginGroup(name.c_str())) - { - const auto& pLight = mpScene->getLight(i); - pLight->renderUI(pGui); - - if (pLight->getType() == LightPoint) - { - setObjectPath(pGui, pLight, "PointLight"); - } - - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Remove")) - { - if (msgBox("Delete light?", MsgBoxType::OkCancel) == MsgBoxButton::Ok) - { - deleteLight(i); - } - } - } - - pGui->endGroup(); - } - } - pGui->endGroup(); - } - } - - void SceneEditor::renderGui(Gui* pGui) - { - pGui->pushWindow("Scene Editor", 400, 600, 20, 300); - if (pGui->addButton("Export Scene")) - { - saveScene(); - } - - // Gizmo Selection - uint32_t selectedGizmo = (uint32_t)mActiveGizmoType; - pGui->addRadioButtons(kGizmoSelectionButtons, selectedGizmo); - setActiveGizmo((Gizmo::Type)selectedGizmo, mSelectedInstances.size() > 0 && mHideWireframe == false); - - pGui->addSeparator(); - renderGlobalElements(pGui); - renderCameraElements(pGui); - renderPathElements(pGui); - renderModelElements(pGui); - renderLightElements(pGui); - - pGui->popWindow(); - - if (mpPathEditor != nullptr) - { - mpPathEditor->render(pGui); - } - } - - void SceneEditor::renderModelAnimation(Gui* pGui) - { - const auto pModel = mpScene->getModelCount() ? mpScene->getModel(mSelectedModel) : nullptr; - - if (pModel && pModel->hasAnimations()) - { - Gui::DropdownList list(pModel->getAnimationsCount() + 1); - list[0].label = "Bind Pose"; - list[0].value = AnimationController::kBindPoseAnimationId; - - for (uint32_t i = 0; i < pModel->getAnimationsCount(); i++) - { - list[i + 1].value = i; - list[i + 1].label = pModel->getAnimationName(i); - if (list[i + 1].label.size() == 0) - { - list[i + 1].label = std::to_string(i); - } - } - uint32_t activeAnim = mpScene->getModel(mSelectedModel)->getActiveAnimation(); - if (pGui->addDropdown(kActiveAnimationStr, list, activeAnim)) mpScene->getModel(mSelectedModel)->setActiveAnimation(activeAnim); - } - } - - void SceneEditor::select(const Scene::ModelInstance::SharedConstPtr& pModelInstance, const Model::MeshInstance::SharedConstPtr& pMeshInstance) - { - // If instance has already been picked, ignore it - if (mSelectedInstances.count(pModelInstance.get()) > 0) - { - return; - } - - deselect(); - - mpSelectionScene->addModelInstance(std::const_pointer_cast(pModelInstance)); - - setActiveGizmo(mActiveGizmoType, mHideWireframe == false); - - // - // Track selection and set corresponding object as selected/active - // - - mSelectedInstances.insert(pModelInstance.get()); - - if (pModelInstance->getObject() == mpCameraModel) - { - mSelectedObjectType = ObjectType::Camera; - - uint32_t cameraID = findEditorModelInstanceID(mEditorCameraModelID, pModelInstance); - if (cameraID != (uint32_t)-1) - { - mpScene->setActiveCamera(cameraID); - } - } - else if (pModelInstance->getObject() == mpLightModel) - { - mSelectedObjectType = ObjectType::Light; - - uint32_t instanceID = findEditorModelInstanceID(mEditorLightModelID, pModelInstance); - if (instanceID != (uint32_t)-1) - { - mSelectedLight = mLightIDEditorToScene[instanceID]; - } - } - else if (pModelInstance->getObject() == mpKeyframeModel) - { - assert(mpPathEditor); - mSelectedObjectType = ObjectType::Keyframe; - - uint32_t frameID = findEditorModelInstanceID(mEditorKeyframeModelID, pModelInstance); - if (frameID != (uint32_t)-1) - { - mpPathEditor->setActiveFrame(frameID); - } - } - else - { - mSelectedObjectType = ObjectType::Model; - setActiveModelInstance(pModelInstance); - - if (pMeshInstance != nullptr) - { - mpSelectedMesh = pMeshInstance->getObject(); - mSelectedMeshString = "Selected Mesh: " + pModelInstance->getObject()->getName() + " - Mesh " + std::to_string(mpSelectedMesh->getId()); - } - } - } - - void SceneEditor::deselect() - { - if (mpSelectionScene) - { - mpSelectionScene->deleteAllModels(); - } - - setActiveGizmo(mActiveGizmoType, false); - - mSelectedInstances.clear(); - mSelectedObjectType = ObjectType::None; - mpSelectedMesh = nullptr; - } - - void SceneEditor::setActiveGizmo(Gizmo::Type type, bool show) - { - if (mGizmos[(uint32_t)type] != nullptr) - { - if (mActiveGizmoType != type) - { - // Hide old gizmo - mGizmos[(uint32_t)mActiveGizmoType]->setVisible(false); - - // Set visibility on new gizmo - mGizmos[(uint32_t)type]->setVisible(show); - } - else - { - // Change visibility on active gizmo - mGizmos[(uint32_t)mActiveGizmoType]->setVisible(show); - } - } - - mActiveGizmoType = type; - } - - uint32_t SceneEditor::findEditorModelInstanceID(uint32_t modelID, const Scene::ModelInstance::SharedConstPtr& pInstance) const - { - for (uint32_t i = 0; i < mpEditorScene->getModelInstanceCount(modelID); i++) - { - if (mpEditorScene->getModelInstance(modelID, i) == pInstance) - { - return i; - } - } - - return (uint32_t)-1; - } - - void SceneEditor::updateEditorModelIDs() - { - mEditorCameraModelID = (uint32_t)-1; - mEditorLightModelID = (uint32_t)-1; - mEditorKeyframeModelID = (uint32_t)-1; - - for (uint32_t i = 0; i < mpEditorScene->getModelCount(); i++) - { - assert(mpEditorScene->getModelInstanceCount(i) > 0); - - const Model::SharedPtr& pModel = mpEditorScene->getModelInstance(i, 0)->getObject(); - if (pModel == mpCameraModel) - { - mEditorCameraModelID = i; - } - else if (pModel == mpLightModel) - { - mEditorLightModelID = i; - } - else if (pModel == mpKeyframeModel) - { - mEditorKeyframeModelID = i; - } - } - } - - void SceneEditor::addModel(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Add Model")) - { - std::string filename; - if (openFileDialog(Model::kFileExtensionFilters, filename)) - { - auto pModel = Model::createFromFile(filename.c_str(), mModelLoadFlags); - if (pModel == nullptr) - { - logError("Error when trying to load model " + filename); - return; - } - - mpScene->addModelInstance(pModel, getUniqueNumberedName(pModel->getName(), 0, mInstanceNames)); - - mSelectedModel = mpScene->getModelCount() - 1; - mSelectedModelInstance = 0; - - mInstanceRotationAngles.emplace_back(); - mInstanceRotationAngles.back().push_back(mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance)->getRotation()); - } - mSceneDirty = true; - } - } - } - - void SceneEditor::deleteModel() - { - // Cleanup individual instances - const uint32_t instanceCount = mpScene->getModelInstanceCount(mSelectedModel); - for (uint32_t i = 0; i < instanceCount; i++) - { - auto& pInstance = mpScene->getModelInstance(mSelectedModel, i); - mInstanceNames.erase(pInstance->getName()); - - // Each detachObjectFromPaths searches through all paths' attached objects - detachObjectFromPaths(pInstance); - } - - mpScene->deleteModel(mSelectedModel); - mInstanceRotationAngles.erase(mInstanceRotationAngles.begin() + mSelectedModel); - mSelectedModel = 0; - mSelectedModelInstance = 0; - mSceneDirty = true; - deselect(); - } - - void SceneEditor::deleteModel(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (mpScene->getModelCount() > 0) - { - if (pGui->addButton("Remove Model")) - { - deleteModel(); - } - } - } - } - - void SceneEditor::addModelInstance(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Add Instance")) - { - const auto& pInstance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - auto& pModel = mpScene->getModel(mSelectedModel); - - // Set new selection index - mSelectedModelInstance = mpScene->getModelInstanceCount(mSelectedModel); - - // Add instance - std::string name = getUniqueNumberedName(pModel->getName(), mSelectedModelInstance, mInstanceNames); - mpScene->addModelInstance(pModel, name, pInstance->getTranslation(), pInstance->getRotation(), pInstance->getScaling()); - - auto& pNewInstance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - mInstanceRotationAngles[mSelectedModel].push_back(pNewInstance->getRotation()); - select(pNewInstance); - - mSceneDirty = true; - } - } - } - - void SceneEditor::addModelInstanceRange(Gui* pGui) - { - if (pGui->addIntVar(kSelectedInstanceStr, mSelectedModelInstance, 0, mpScene->getModelInstanceCount(mSelectedModel) - 1)) - { - select(mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance)); - } - } - - void SceneEditor::deleteModelInstance(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Remove Instance")) - { - if (mpScene->getModelInstanceCount(mSelectedModel) == 1) - { - auto MbRes = msgBox("The active model has a single instance. Removing it will remove the model from the scene.\nContinue?", MsgBoxType::OkCancel); - if (MbRes == MsgBoxButton::Ok) - { - deleteModel(); - return; - } - } - - const auto& pInstance = mpScene->getModelInstance(mSelectedModel, mSelectedModelInstance); - - detachObjectFromPaths(pInstance); - mInstanceNames.erase(pInstance->getName()); - - mpScene->deleteModelInstance(mSelectedModel, mSelectedModelInstance); - - auto& modelRotations = mInstanceRotationAngles[mSelectedModel]; - modelRotations.erase(modelRotations.begin() + mSelectedModelInstance); - - deselect(); - - mSelectedModelInstance = 0; - mSceneDirty = true; - } - } - } - - void SceneEditor::addCamera(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Add Camera")) - { - auto pCamera = Camera::create(); - auto pActiveCamera = mpScene->getActiveCamera(); - if (pActiveCamera) - { - *pCamera = *pActiveCamera; - } - pCamera->setName(getUniqueNumberedName("Camera", mpScene->getCameraCount(), mCameraNames)); - - const uint32_t camIndex = mpScene->addCamera(pCamera); - mpScene->setActiveCamera(camIndex); - - // Update editor scene - mpEditorScene->addModelInstance(mpCameraModel, pCamera->getName(), glm::vec3(), glm::vec3(), glm::vec3(kCameraModelScale)); - updateEditorModelIDs(); - mCameraNames.emplace(pCamera->getName()); - - select(mpEditorScene->getModelInstance(mEditorCameraModelID, camIndex)); - - mSceneDirty = true; - } - } - } - - void SceneEditor::deleteCamera(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Remove Camera")) - { - detachObjectFromPaths(mpScene->getActiveCamera()); - mCameraNames.erase(mpScene->getActiveCamera()->getName()); - mpScene->deleteCamera(mpScene->getActiveCameraIndex()); - - mpEditorScene->deleteModelInstance(mEditorCameraModelID, mpScene->getActiveCameraIndex()); - updateEditorModelIDs(); - deselect(); - - mSceneDirty = true; - } - } - } - - void SceneEditor::addPath(Gui* pGui) - { - if (mpPathEditor == nullptr) - { - if (pGui->addButton("Add Path")) - { - auto pPath = ObjectPath::create(); - pPath->setName("Path " + std::to_string(mpScene->getPathCount())); - mSelectedPath = mpScene->addPath(pPath); - - startPathEditor(); - mSceneDirty = true; - } - } - } - - void SceneEditor::deletePath(Gui* pGui) - { - if (mpPathEditor == nullptr && mpScene->getPathCount() > 0) - { - if (pGui->addButton("Delete Path", true)) - { - auto& pPath = mpScene->getPath(mSelectedPath); - for (uint32_t i = 0; i < pPath->getAttachedObjectCount(); i++) - { - mObjToPathMap.erase(pPath->getAttachedObject(i).get()); - } - - mpScene->deletePath(mSelectedPath); - - if (mSelectedPath == mpScene->getPathCount()) - { - mSelectedPath = mpScene->getPathCount() - 1; - } - - mSceneDirty = true; - } - } - } - - void SceneEditor::pathEditorFrameChangedCB() - { - const uint32_t activeFrameID = mpPathEditor->getActiveFrame(); - - // When active frame ID changed, select a different keyframe in editor - const auto& pKeyframeInstance = mpEditorScene->getModelInstance(mEditorKeyframeModelID, activeFrameID); - select(pKeyframeInstance); - - // When properties of active frame changed, update the model representing it - const auto& frame = mpScene->getPath(mSelectedPath)->getKeyFrame(activeFrameID); - pKeyframeInstance->setTranslation(frame.position, false); - pKeyframeInstance->setTarget(frame.target); - pKeyframeInstance->setUpVector(frame.up); - } - - void SceneEditor::pathEditorFrameAddRemoveCB() - { - if (mSelectedObjectType == ObjectType::Keyframe) - { - deselect(); - } - - removeSelectedPathKeyframeModels(); - addSelectedPathKeyframeModels(); - } - - void SceneEditor::pathEditorFinishedCB() - { - deselect(); - - if (mpPathEditor->getPath()->getKeyFrameCount() > 0) - { - removeSelectedPathKeyframeModels(); - } - - mpPathEditor = nullptr; - } - - void SceneEditor::addSelectedPathKeyframeModels() - { - assert(mpPathEditor != nullptr); - - const auto& pPath = mpPathEditor->getPath(); - - if (pPath->getKeyFrameCount() > 0) - { - // Add models to represent keyframes - for (uint32_t i = 0; i < pPath->getKeyFrameCount(); i++) - { - const auto& frame = pPath->getKeyFrame(i); - auto pNewInstance = Scene::ModelInstance::create(mpKeyframeModel, frame.position, frame.target, frame.up, glm::vec3(kKeyframeModelScale), "Frame " + std::to_string(i)); - mpEditorScene->addModelInstance(pNewInstance); - } - - updateEditorModelIDs(); - } - } - - void SceneEditor::removeSelectedPathKeyframeModels() - { - assert(mpPathEditor != nullptr); - - if (mEditorKeyframeModelID != (uint32_t)-1) - { - // Remove keyframe models - mpEditorScene->deleteModel(mEditorKeyframeModelID); - updateEditorModelIDs(); - } - } - - void SceneEditor::startPathEditor() - { - const auto& pPath = mpScene->getPath(mSelectedPath); - mpPathEditor = PathEditor::create(pPath, - mpEditorScene->getActiveCamera(), - [this]() { pathEditorFrameChangedCB(); }, - [this]() { pathEditorFrameAddRemoveCB(); }, - [this]() { pathEditorFinishedCB(); }); - - addSelectedPathKeyframeModels(); - - if (pPath->getKeyFrameCount() > 0) - { - select(mpEditorScene->getModelInstance(mEditorKeyframeModelID, 0)); - } - - mSceneDirty = true; - } - - void SceneEditor::startPathEditor(Gui* pGui) - { - if (mpPathEditor == nullptr && mpScene->getPathCount() > 0) - { - if (pGui->addButton("Edit Path", true)) - { - startPathEditor(); - } - } - } - - void SceneEditor::setObjectPath(Gui* pGui, const IMovableObject::SharedPtr& pMovable, const std::string& objType) - { - // Find what path this pMovable is set to, if any - ObjectPath::SharedPtr pOldPath; - uint32_t oldPathID = Scene::kNoPath; - - auto it = mObjToPathMap.find(pMovable.get()); - if (it != mObjToPathMap.end()) - { - pOldPath = it->second; - } - - // Find path ID - if (pOldPath != nullptr) - { - for (uint32_t i = 0; i < mpScene->getPathCount(); i++) - { - if (mpScene->getPath(i) == pOldPath) - { - oldPathID = i; - } - } - } - - // Append tag to avoid hash collisions in imgui. ##Tag does not appear when rendered - std::string label = std::string(kSelectedPathStr) + "##" + objType; - - uint32_t newPathID = oldPathID; - if (pGui->addDropdown(label.c_str(), getPathDropdownList(mpScene.get(), true), newPathID)) - { - // Detach from old path - if (oldPathID != Scene::kNoPath) - { - pOldPath->detachObject(pMovable); - mObjToPathMap.erase(pMovable.get()); - - // #TODO Find a way to work with base/movable matrices on object instances. - // Cameras and Lights attach directly to paths instead of having a separate matrix - - // Reset movable matrix on instances when detaching from path - if (std::dynamic_pointer_cast(pMovable) != nullptr) - { - pMovable->move(glm::vec3(), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - } - } - - // Attach to new path - if (newPathID != Scene::kNoPath) - { - const auto& pNewPath = mpScene->getPath(newPathID); - pNewPath->attachObject(pMovable); - mObjToPathMap[pMovable.get()] = pNewPath; - } - } - } -} diff --git a/Framework/Source/Graphics/Scene/Editor/SceneEditor.h b/Framework/Source/Graphics/Scene/Editor/SceneEditor.h deleted file mode 100644 index b70445b22..000000000 --- a/Framework/Source/Graphics/Scene/Editor/SceneEditor.h +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "Graphics/Paths/PathEditor.h" -#include "Utils/DebugDrawer.h" -#include "Utils/Picking/Picking.h" -#include "Graphics/Scene/Editor/Gizmo.h" -#include "Graphics/Scene/Editor/SceneEditorRenderer.h" - -namespace Falcor -{ - class Scene; - class Gui; - - /** Used by the Scene Editor utility app to edit scenes. Contains a separate internal Scene instance to manage - editor-specific objects such as light bulbs, camera models, and gizmos. - */ - class SceneEditor - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - /** Create a Scene Editor instance - \param[in] pScene Scene to edit - \param[in] modelLoadFlags Flags to use when adding new models to the scene - */ - static UniquePtr create(const Scene::SharedPtr& pScene, Model::LoadFlags modelLoadFlags = Model::LoadFlags::None); - ~SceneEditor(); - - /** Get the internal camera used for rendering editor-specific objects. This is also typically the - camera used to render the application in the SceneEditor utility app. - */ - const Camera::SharedPtr getEditorCamera() const { return mpEditorScene->getActiveCamera(); } - - /** Updates the internal scene containing editor-specific objects - */ - void update(double currentTime); - - /** Render the scene editor - \param[in] pContext Render context - */ - void render(RenderContext* pContext); - - /** Render the editor's UI elements - \param[in] pGui GUI instance to render the editor UI with - */ - void renderGui(Gui* pGui); - - /** Handles mouse events including object picking. - \param[in] pContext Render context. Used for object picking - \param[in] mouseEvent Mouse event - */ - bool onMouseEvent(RenderContext* pContext, const MouseEvent& mouseEvent); - - /** Handles keyboard events - */ - bool onKeyEvent(const KeyboardEvent& keyEvent); - - /** Updates the internal graphics objects used by the editor. Call this whenever Sample::onResizeSwapChain() is called. - */ - void onResizeSwapChain(); - - private: - - SceneEditor(const Scene::SharedPtr& pScene, Model::LoadFlags modelLoadFlags); - Scene::SharedPtr mpScene; - - bool mSceneDirty = false; - - // Main GUI functions - void renderModelElements(Gui* pGui); - void renderCameraElements(Gui* pGui); - void renderLightElements(Gui* pGui); - void renderGlobalElements(Gui* pGui); - void renderPathElements(Gui* pGui); - - // Model functions - void addModel(Gui* pGui); - void deleteModel(Gui* pGui); - void deleteModel(); - void setModelName(Gui* pGui); - void setShadingModel(Gui* pGui); - void setModelVisible(Gui* pGui); - void selectActiveModel(Gui* pGui); - - // Model instance - void addModelInstance(Gui* pGui); - void addModelInstanceRange(Gui* pGui); - void deleteModelInstance(Gui* pGui); - void setInstanceTranslation(Gui* pGui); - void setInstanceScaling(Gui* pGui); - void setInstanceRotation(Gui* pGui); - - // Camera functions - void setCameraName(Gui* pGui); - void setCameraSpeed(Gui* pGui); - void addCamera(Gui* pGui); - void deleteCamera(Gui* pGui); - void setActiveCamera(Gui* pGui); - - // Light functions - void deleteLight(uint32_t id); - void addPointLight(Gui* pGui); - void addDirectionalLight(Gui* pGui); - - // Paths - void addPath(Gui* pGui); - void selectPath(Gui* pGui); - void deletePath(Gui* pGui); - void startPathEditor(Gui* pGui); - void startPathEditor(); - void setObjectPath(Gui* pGui, const IMovableObject::SharedPtr& pMovable, const std::string& objType); - - // Global functions - void saveScene(); - - void renderModelAnimation(Gui* pGui); - - Model::LoadFlags mModelLoadFlags = Model::LoadFlags::None; - Scene::LoadFlags mSceneLoadFlags = Scene::LoadFlags::None; - - // - // Initialization - // - - // Initializes Editor helper-scenes, Picking, and Rendering - void initializeEditorRendering(); - - // Initializes Editor's representation of the scene being edited - void initializeEditorObjects(); - - // Model Instance Rotation Angle Helpers - const glm::vec3& getActiveInstanceRotationAngles(); - void setActiveInstanceRotationAngles(const glm::vec3& rotation); - std::vector> mInstanceRotationAngles; - - // - // Editor Objects - // - - static const float kCameraModelScale; - static const float kLightModelScale; - static const float kKeyframeModelScale; - - // Update transform of gizmos and camera models - void updateEditorObjectTransforms(); - - // Helper function to update the transform of a single camera model from it's respective camera in the master scene - void updateCameraModelTransform(uint32_t cameraID); - - // Rebuilds the lookup map between light model instances and master scene point lights - void rebuildLightIDMap(); - - // Helper to apply gizmo transforms to the object being edited - void applyGizmoTransform(); - - // Types of objects selectable in the editor - enum class ObjectType - { - None, - Model, - Camera, - Light, - Keyframe - }; - - std::string getUniqueNumberedName(const std::string& baseName, uint32_t idSuffix, const std::set& nameMap) const; - - std::set mInstanceNames; - std::set mCameraNames; - std::set mLightNames; - - // - // Picking - // - - void select(const Scene::ModelInstance::SharedConstPtr& pModelInstance, const Model::MeshInstance::SharedConstPtr& pMeshInstance = nullptr); - void deselect(); - - void setActiveModelInstance(const Scene::ModelInstance::SharedConstPtr& pModelInstance); - - // ID's in master scene - uint32_t mSelectedModel = 0; - int32_t mSelectedModelInstance = 0; - uint32_t mSelectedCamera = 0; - uint32_t mSelectedLight = 0; - uint32_t mSelectedPath = 0; - - Picking::UniquePtr mpScenePicker; - - std::set mSelectedInstances; - ObjectType mSelectedObjectType = ObjectType::None; - - CpuTimer mMouseHoldTimer; - - // - // Gizmos - // - - static const Gui::RadioButtonGroup kGizmoSelectionButtons; - - void setActiveGizmo(Gizmo::Type type, bool show); - - bool mGizmoBeingDragged = false; - Gizmo::Type mActiveGizmoType = Gizmo::Type::Translate; - Gizmo::Gizmos mGizmos; - - // - // Editor - // - - // Find instance ID of a model instance in the editor scene. Returns uint -1 if not found. - uint32_t findEditorModelInstanceID(uint32_t modelID, const Scene::ModelInstance::SharedConstPtr& pInstance) const; - // Update Camera, Lightbulb, and Keyframe model ID's in the Editor Objects Scene - void updateEditorModelIDs(); - - // Wireframe Rendering - bool mHideWireframe = false; - GraphicsState::SharedPtr mpSelectionGraphicsState; - GraphicsProgram::SharedPtr mpColorProgram; - GraphicsVars::SharedPtr mpColorProgramVars; - - // Separate scene for rendering selected model wireframe - Scene::SharedPtr mpSelectionScene; - SceneRenderer::SharedPtr mpSelectionSceneRenderer; - - // Separate scene for editor gizmos and objects - Scene::SharedPtr mpEditorScene; - SceneEditorRenderer::UniquePtr mpEditorSceneRenderer; - Picking::UniquePtr mpEditorPicker; - - Model::SharedPtr mpCameraModel; - Model::SharedPtr mpLightModel; - Model::SharedPtr mpKeyframeModel; - - uint32_t mEditorCameraModelID = (uint32_t)-1; - uint32_t mEditorLightModelID = (uint32_t)-1; - uint32_t mEditorKeyframeModelID = (uint32_t)-1; - - // Maps between light models and master scene light ID - std::unordered_map mLightIDEditorToScene; - std::unordered_map mLightIDSceneToEditor; - - std::string mSelectedMeshString; - Mesh::SharedPtr mpSelectedMesh; - - const static Gui::DropdownList kShadingModelList; - - // - // Paths - // - void renderPath(RenderContext* pContext); - - void detachObjectFromPaths(const IMovableObject::SharedPtr& pMovable); - - void addSelectedPathKeyframeModels(); - void removeSelectedPathKeyframeModels(); - - // When path editor closes - void pathEditorFinishedCB(); - - // When switching to a new active frame, or if active frame properties are changed - void pathEditorFrameChangedCB(); - - // When frames are added or removed - void pathEditorFrameAddRemoveCB(); - - bool mRenderAllPaths = false; - - PathEditor::UniquePtr mpPathEditor; - std::unordered_map mObjToPathMap; - - DebugDrawer::SharedPtr mpDebugDrawer; - - GraphicsState::SharedPtr mpPathGraphicsState; - GraphicsProgram::SharedPtr mpDebugDrawProgram; - GraphicsVars::SharedPtr mpDebugDrawProgramVars; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.cpp b/Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.cpp deleted file mode 100644 index b2ed40279..000000000 --- a/Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Graphics/Scene/Editor/SceneEditorRenderer.h" - -namespace Falcor -{ - SceneEditorRenderer::UniquePtr SceneEditorRenderer::create(const Scene::SharedPtr& pScene) - { - return UniquePtr(new SceneEditorRenderer(pScene)); - } - - void SceneEditorRenderer::renderScene(RenderContext* pContext, Camera* pCamera) - { - mpGraphicsState->setFbo(pContext->getGraphicsState()->getFbo()); - pContext->setGraphicsState(mpGraphicsState); - pContext->setGraphicsVars(mpProgramVars); - SceneRenderer::renderScene(pContext, pCamera); - } - - void SceneEditorRenderer::registerGizmos(const Gizmo::Gizmos& gizmos) - { - mGizmos = gizmos; - } - - SceneEditorRenderer::SceneEditorRenderer(const Scene::SharedPtr& pScene) - : SceneRenderer(pScene) - { - mpGraphicsState = GraphicsState::create(); - - // Solid Rasterizer state - RasterizerState::Desc rsDesc; - rsDesc.setFillMode(RasterizerState::FillMode::Solid).setCullMode(RasterizerState::CullMode::Back); - mpGraphicsState->setRasterizerState(RasterizerState::create(rsDesc)); - - // Depth test - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(false).setStencilTest(true).setStencilRef(1); - dsDesc.setStencilOp(DepthStencilState::Face::FrontAndBack, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Replace); - mpSetStencilDS = DepthStencilState::create(dsDesc); - - dsDesc.setDepthTest(true).setStencilTest(true).setStencilFunc(DepthStencilState::Face::FrontAndBack, DepthStencilState::Func::NotEqual); - dsDesc.setStencilOp(DepthStencilState::Face::FrontAndBack, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Keep); - mpExcludeStencilDS = DepthStencilState::create(dsDesc); - - // Shader - Program::DefineList defines; - defines.add("SHADING"); - mpProgram = GraphicsProgram::createFromFile("Framework/Shaders/SceneEditor.slang", "editorVs", "editorPs", defines); - mpProgramVars = GraphicsVars::create(mpProgram->getReflector()); - - defines.add("CULL_REAR_SECTION"); - mpRotGizmoProgram = GraphicsProgram::createFromFile("Framework/Shaders/SceneEditor.slang", "editorVs", "editorPs", defines); - } - - void SceneEditorRenderer::setPerFrameData(const CurrentWorkingData& currentData) - { - if (currentData.pCamera) - { - // Set camera for regular shader - ConstantBuffer* pCB = mpProgramVars->getConstantBuffer(kPerFrameCbName).get(); - currentData.pCamera->setIntoConstantBuffer(pCB, sCameraDataOffset); - } - } - - bool SceneEditorRenderer::setPerModelInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, uint32_t instanceID) - { - const Gizmo::Type gizmoType = Gizmo::getGizmoType(mGizmos, currentData.pModel); - - if (gizmoType != Gizmo::Type::Invalid) - { - assert(instanceID < 3); - glm::vec3 color; - color[instanceID] = 1.0f; - - mpGraphicsState->setDepthStencilState(mpSetStencilDS); - mpProgramVars["ConstColorCB"]["gColor"] = color; - - // For rotation gizmo, set shader to cut out away-facing parts - if (gizmoType == Gizmo::Type::Rotate) - { - mpGraphicsState->setProgram(mpRotGizmoProgram); - return true; - } - } - else - { - mpGraphicsState->setDepthStencilState(mpExcludeStencilDS); - mpProgramVars["ConstColorCB"]["gColor"] = glm::vec3(0.6f); - } - - mpGraphicsState->setProgram(mpProgram); - - return true; - } -} diff --git a/Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.h b/Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.h deleted file mode 100644 index 5f58497c2..000000000 --- a/Framework/Source/Graphics/Scene/Editor/SceneEditorRenderer.h +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once - -#include "Graphics/Scene/SceneRenderer.h" -#include "Graphics/Scene/Editor/Gizmo.h" -#include - -namespace Falcor -{ - /** Used by the SceneEditor to render helper objects. - */ - class SceneEditorRenderer : public SceneRenderer - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - static UniquePtr create(const Scene::SharedPtr& pScene); - - void renderScene(RenderContext* pContext, Camera* pCamera); - - /** Informs the renderer what gizmos exist in order to color them appropriately when rendering - */ - void registerGizmos(const Gizmo::Gizmos& gizmos); - - private: - SceneEditorRenderer(const Scene::SharedPtr& pScene); - - virtual void setPerFrameData(const CurrentWorkingData& currentData) override; - virtual bool setPerModelInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, uint32_t instanceID); - Gizmo::Gizmos mGizmos; - - GraphicsProgram::SharedPtr mpProgram; - GraphicsProgram::SharedPtr mpRotGizmoProgram; - GraphicsVars::SharedPtr mpProgramVars; - GraphicsState::SharedPtr mpGraphicsState; - - DepthStencilState::SharedPtr mpSetStencilDS; - DepthStencilState::SharedPtr mpExcludeStencilDS; - }; -} - diff --git a/Framework/Source/Graphics/Scene/Scene.cpp b/Framework/Source/Graphics/Scene/Scene.cpp deleted file mode 100644 index 8cb68d325..000000000 --- a/Framework/Source/Graphics/Scene/Scene.cpp +++ /dev/null @@ -1,441 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Scene.h" -#include "SceneImporter.h" -#include "glm/gtx/euler_angles.hpp" -#include "glm/gtc/matrix_transform.hpp" -#include "Utils/Gui.h" -#include "Graphics/TextureHelper.h" - -namespace Falcor -{ - uint32_t Scene::sSceneCounter = 0; - - const Scene::UserVariable Scene::kInvalidVar; - - const FileDialogFilterVec Scene::kFileExtensionFilters = { {"fscene", "Falcor Scene Files"} }; - - Scene::SharedPtr Scene::loadFromFile(const std::string& filename, Model::LoadFlags modelLoadFlags, Scene::LoadFlags sceneLoadFlags) - { - Scene::SharedPtr pScene = create(filename); - if (SceneImporter::loadScene(*pScene, filename, modelLoadFlags, sceneLoadFlags) == false) - { - pScene = nullptr; - } - return pScene; - } - - Scene::SharedPtr Scene::create(const std::string& filename) - { - return SharedPtr(new Scene(filename)); - } - - Scene::Scene(const std::string& filename) : mId(sSceneCounter++), mFilename(filename) - { - // Reset all global id counters recursively - Model::resetGlobalIdCounter(); - } - - Scene::~Scene() = default; - - void Scene::updateExtents() - { - if (mExtentsDirty) - { - mExtentsDirty = false; - - BoundingBox sceneAABB; - bool first = true; - for (uint32_t i = 0; i < getModelCount(); ++i) - { - for (uint32_t j = 0; j < getModelInstanceCount(i); ++j) - { - const auto& pInst = getModelInstance(i, j); - if (first) - { - sceneAABB = pInst->getBoundingBox(); - first = false; - } - else - { - sceneAABB = BoundingBox::fromUnion(sceneAABB, pInst->getBoundingBox()); - } - } - } - - mCenter = sceneAABB.center; - mRadius = length(sceneAABB.extent); - mBoundingBox = sceneAABB; - - // Update light extents - for (auto& pLight : mpLights) - { - if (pLight->getType() == LightDirectional) - { - auto pDirLight = std::dynamic_pointer_cast(pLight); - pDirLight->setWorldParams(getCenter(), getRadius()); - } - } - } - } - - bool Scene::update(double currentTime, CameraController* cameraController) - { - bool changed = false; - for (auto& path : mpPaths) - { - if (path->animate(currentTime)) - { - changed = true; - } - } - - for (uint32_t i = 0; i < mModels.size(); i++) - { - if (mModels[i][0]->getObject()->animate(currentTime)) - { - changed = true; - } - } - - mExtentsDirty = mExtentsDirty || changed; - - if (getCameraCount() > 0) - { - getActiveCamera()->beginFrame(); - } - - // Ignore the elapsed time we got from the user. This will allow camera movement in cases where the time is frozen - if (cameraController) - { - cameraController->attachCamera(getActiveCamera()); - cameraController->setCameraSpeed(getCameraSpeed()); - changed |= cameraController->update(); - } - - return changed; - } - - void Scene::deleteModel(uint32_t modelID) - { - // Delete entire vector of instances - mModels.erase(mModels.begin() + modelID); - mExtentsDirty = true; - } - - void Scene::deleteAllModels() - { - mModels.clear(); - mExtentsDirty = true; - } - - uint32_t Scene::getModelInstanceCount(uint32_t modelID) const - { - return (uint32_t)(mModels[modelID].size()); - } - - void Scene::addModelInstance(const Model::SharedPtr& pModel, const std::string& instanceName, const glm::vec3& translation, const glm::vec3& yawPitchRoll, const glm::vec3& scaling) - { - ModelInstance::SharedPtr pInstance = ModelInstance::create(pModel, translation, yawPitchRoll, scaling, instanceName); - addModelInstance(pInstance); - mExtentsDirty = true; - } - - void Scene::addModelInstance(const ModelInstance::SharedPtr& pInstance) - { - // Checking for existing instance list for model - for (uint32_t modelID = 0; modelID < (uint32_t)mModels.size(); modelID++) - { - // If found, add to that list - if (getModel(modelID) == pInstance->getObject()) - { - mModels[modelID].push_back(pInstance); - return; - } - } - - // If not found, add a new list - mModels.emplace_back(); - mModels.back().push_back(pInstance); - mExtentsDirty = true; - } - - void Scene::deleteModelInstance(uint32_t modelID, uint32_t instanceID) - { - // Delete instance - auto& instances = mModels[modelID]; - - // Check if there is only one instance left. - if (instances.size() == 1) - { - // Delete the entire model, since it will also erase the corresponding instance. - assert(instanceID == 0); - deleteModel(modelID); - } - else - { - // Erase the instance. - instances.erase(instances.begin() + instanceID); - } - - // Extents will be dirty in either case. - mExtentsDirty = true; - } - - const Scene::UserVariable& Scene::getUserVariable(const std::string& name) const - { - const auto& a = mUserVars.find(name); - if (a == mUserVars.end()) - { - logWarning("Can't find user variable " + name + " in scene."); - return kInvalidVar; - } - else - { - return a->second; - } - } - - const Scene::UserVariable& Scene::getUserVariable(uint32_t varID, std::string& varName) const - { - for (const auto& a : mUserVars) - { - if (varID == 0) - { - varName = a.first; - return a.second; - } - --varID; - } - - should_not_get_here(); - varName = ""; - return mUserVars.begin()->second; - } - - uint32_t Scene::addLight(const Light::SharedPtr& pLight) - { - if (pLight->getType() == LightArea) - { - logWarning("Use Scene::addAreaLight() for area lights."); - return uint32(-1); - } - - mpLights.push_back(pLight); - mExtentsDirty = true; - return (uint32_t)mpLights.size() - 1; - } - - void Scene::deleteLight(uint32_t lightID) - { - mpLights.erase(mpLights.begin() + lightID); - mExtentsDirty = true; - } - - uint32_t Scene::addLightProbe(const LightProbe::SharedPtr& pLightProbe) - { - mpLightProbes.push_back(pLightProbe); - return (uint32_t)mpLightProbes.size() - 1; - } - - void Scene::deleteLightProbe(uint32_t lightID) - { - mpLightProbes.erase(mpLightProbes.begin() + lightID); - } - - uint32_t Scene::addAreaLight(const AreaLight::SharedPtr& pAreaLight) - { - mpAreaLights.push_back(pAreaLight); - return (uint32_t)mpAreaLights.size() - 1; - } - - void Scene::deleteAreaLight(uint32_t lightID) - { - mpAreaLights.erase(mpAreaLights.begin() + lightID); - } - - uint32_t Scene::addPath(const ObjectPath::SharedPtr& pPath) - { - mpPaths.push_back(pPath); - return (uint32_t)mpPaths.size() - 1; - } - - void Scene::deletePath(uint32_t pathID) - { - mpPaths.erase(mpPaths.begin() + pathID); - } - - uint32_t Scene::addCamera(const Camera::SharedPtr& pCamera) - { - mCameras.push_back(pCamera); - return (uint32_t)mCameras.size() - 1; - } - - void Scene::deleteCamera(uint32_t cameraID) - { - mCameras.erase(mCameras.begin() + cameraID); - - if (cameraID == mActiveCameraID) - { - mActiveCameraID = 0; - } - } - - void Scene::setActiveCamera(uint32_t camID) - { - mActiveCameraID = camID; - } - - void Scene::merge(const Scene* pFrom) - { -#define merge(name_) name_.insert(name_.end(), pFrom->name_.begin(), pFrom->name_.end()); - - merge(mModels); - merge(mpLights); - merge(mpPaths); - merge(mCameras); -#undef merge - mUserVars.insert(pFrom->mUserVars.begin(), pFrom->mUserVars.end()); - mExtentsDirty = true; - } - - void Scene::createAreaLights() - { - // Clean up area light(s) before adding - mpAreaLights.clear(); - - // Go through all models in the scene - for (uint32_t modelId = 0; modelId < getModelCount(); ++modelId) - { - const Model::SharedPtr& pModel = getModel(modelId); - if (pModel) - { - // TODO: Create area lights per model instance - std::vector areaLights = createAreaLightsForModel(pModel.get()); - mpAreaLights.insert(mpAreaLights.end(), areaLights.begin(), areaLights.end()); - } - } - } - - void Scene::bindSampler(Sampler::SharedPtr pSampler) - { - for (auto& model : mModels) - { - model[0]->getObject()->bindSamplerToMaterials(pSampler); - } - - for (auto& probe : mpLightProbes) - { - probe->setSampler(pSampler); - } - } - - void Scene::attachSkinningCacheToModels(SkinningCache::SharedPtr pSkinningCache) - { - for (auto& model : mModels) - { - model[0]->getObject()->attachSkinningCache(pSkinningCache); - } - } - - void Scene::setCamerasAspectRatio(float ratio) - { - for (auto& c : mCameras) c->setAspectRatio(ratio); - } - - void detachCameraFromPaths(const Camera::SharedPtr& pCamera, const std::vector& paths) - { - for (auto& pPath : paths) pPath->detachObject(pCamera); - } - - void Scene::renderUI(Gui* pGui, const char* uiGroup) - { - if (uiGroup == nullptr || pGui->beginGroup(uiGroup)) - { - - pGui->addFloatVar("Scene Unit", mSceneUnit, 0); - pGui->addTooltip("Scene unit in meters"); - - // Cameras (active, speed - if (pGui->beginGroup("Cameras")) - { - pGui->addIntVar("Active Camera", (int32_t&)mActiveCameraID, 0, (int32_t)mCameras.size() - 1); - - if(mpPaths.size()) - { - pGui->addSeparator(); - pGui->addText("Camera Paths"); - if (pGui->addButton("Detach")) detachCameraFromPaths(mCameras[mActiveCameraID], mpPaths); - pGui->addTooltip("Detach the active camera from every path it is attached to"); - if (pGui->addButton("Attach To", true)) mpPaths[mSelectedPath]->attachObject(mCameras[mActiveCameraID]); - pGui->addIntVar("##SelectedPath", (int32_t&)mSelectedPath, 0, (int32_t)mpPaths.size() - 1, 1, true); - pGui->addSeparator(); - } - - pGui->addFloatVar("Camera Speed", mCameraSpeed, 0); - mCameras[mActiveCameraID]->renderUI(pGui); - pGui->endGroup(); - } - - if (pGui->beginGroup("Lights")) - { - uint32_t i = 0; - auto lightUI = [&i, pGui](auto& lightVec) - { - for (auto& pLight : lightVec) - { - std::string label = light_type_string(pLight->getType()) + std::to_string(i++); - pLight->renderUI(pGui, label.c_str()); - } - }; - lightUI(mpLights); - lightUI(mpAreaLights); - - for (auto& pLight : mpLightProbes) - { - std::string label = "LightProbe" + std::to_string(i++); - pLight->renderUI(pGui, label.c_str()); - } - - if (pGui->addButton("Load EnvMap")) - { - std::string filename; - if (openFileDialog(Bitmap::getFileDialogFilters(), filename)) - { - Texture::SharedPtr pEnvMap = createTextureFromFile(filename, false, true); - if (pEnvMap) setEnvironmentMap(pEnvMap); - } - } - - pGui->endGroup(); - } - if (uiGroup) pGui->endGroup(); - } - } -} diff --git a/Framework/Source/Graphics/Scene/Scene.h b/Framework/Source/Graphics/Scene/Scene.h deleted file mode 100644 index a40565383..000000000 --- a/Framework/Source/Graphics/Scene/Scene.h +++ /dev/null @@ -1,298 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include -#include "Graphics/Model/Model.h" -#include "Graphics/Light.h" -#include "Graphics/LightProbe.h" -#include "Graphics/Camera/Camera.h" -#include "Graphics/Camera/CameraController.h" -#include "Graphics/Paths/ObjectPath.h" -#include "Graphics/Model/ObjectInstance.h" -#include "Graphics/Model/SkinningCache.h" - -namespace Falcor -{ - class Gui; - - class Scene : public std::enable_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - static const FileDialogFilterVec kFileExtensionFilters; - - struct UserVariable - { - enum class Type - { - Unknown, // Indicates an invalid/uninitialized variable - Int, - Uint, - Int64, - Uint64, - Double, - String, - Vec2, - Vec3, - Vec4, - Bool, - Vector, - }; - - Type type = Type::Unknown; - union - { - int32_t i32; - uint32_t u32; - int64_t i64; - uint64_t u64; - double d64; - bool b; - }; - std::string str; - glm::vec2 vec2; - glm::vec3 vec3; - glm::vec4 vec4; - std::vector vector; - - UserVariable() { } - UserVariable(const uint32_t& v) : u32(v), type(Type::Uint) { } - UserVariable(const int32_t& v) : i32(v), type(Type::Int) { } - UserVariable(const float& v) : d64((double)v), type(Type::Double) { } - UserVariable(const glm::vec2& v) : vec2(v), type(Type::Vec2) { } - UserVariable(const glm::vec3& v) : vec3(v), type(Type::Vec3) { } - UserVariable(const std::string& s) : str(s), type(Type::String) { } - }; - - using ModelInstance = ObjectInstance; - using ModelInstanceList = std::vector; - - /** - Enum to generate light source(s) - */ - enum class LoadFlags - { - None = 0x0, - GenerateAreaLights = 0x1, ///< Create area light(s) for meshes that have emissive material - }; - - static Scene::SharedPtr loadFromFile(const std::string& filename, Model::LoadFlags modelLoadFlags = Model::LoadFlags::None, Scene::LoadFlags sceneLoadFlags = LoadFlags::None); - static Scene::SharedPtr create(const std::string& filename = ""); - - virtual ~Scene(); - - // Models - uint32_t getModelCount() const { return (uint32_t)mModels.size(); } - const Model::SharedPtr& getModel(uint32_t modelID) const { return mModels[modelID][0]->getObject(); }; - void deleteModel(uint32_t modelID); - void deleteAllModels(); - - // Model Instances - virtual void addModelInstance(const ModelInstance::SharedPtr& pInstance); - void addModelInstance(const Model::SharedPtr& pModel, const std::string& instanceName, const glm::vec3& translation = glm::vec3(), const glm::vec3& yawPitchRoll = glm::vec3(), const glm::vec3& scaling = glm::vec3(1)); - // Adds a model instance and shares ownership of it - uint32_t getModelInstanceCount(uint32_t modelID) const; - const ModelInstance::SharedPtr& getModelInstance(uint32_t modelID, uint32_t instanceID) const { return mModels[modelID][instanceID]; }; - void deleteModelInstance(uint32_t modelID, uint32_t instanceID); - - // Light Sources - uint32_t addLight(const Light::SharedPtr& pLight); - void deleteLight(uint32_t lightID); - uint32_t getLightCount() const { return (uint32_t)mpLights.size(); } - const Light::SharedPtr& getLight(uint32_t index) const { return mpLights[index]; } - const std::vector& getLights() const { return mpLights; } - - // Light Probes - uint32_t addLightProbe(const LightProbe::SharedPtr& pLightProbe); - void deleteLightProbe(uint32_t lightID); - uint32_t getLightProbeCount() const { return (uint32_t)mpLightProbes.size(); } - const LightProbe::SharedPtr& getLightProbe(uint32_t index) const { return mpLightProbes[index]; } - const std::vector& getLightProbes() const { return mpLightProbes; } - - // Area Lights - uint32_t addAreaLight(const AreaLight::SharedPtr& pAreaLight); - void deleteAreaLight(uint32_t lightID); - uint32_t getAreaLightCount() const { return (uint32_t)mpAreaLights.size(); } - const AreaLight::SharedPtr& getAreaLight(uint32_t index) const { return mpAreaLights[index]; } - const std::vector& getAreaLights() const { return mpAreaLights; } - - float getLightingScale() const { return mLightingScale; } - void setLightingScale(float lightingScale) { mLightingScale = lightingScale; } - - // Object Paths - uint32_t addPath(const ObjectPath::SharedPtr& pPath); - void deletePath(uint32_t pathID); - - const ObjectPath::SharedPtr& getPath(uint32_t pathID) const { return mpPaths[pathID]; } - uint32_t getPathCount() const { return (uint32_t)mpPaths.size(); } - - // Camera - uint32_t addCamera(const Camera::SharedPtr& pCamera); - void deleteCamera(uint32_t cameraID); - - uint32_t getCameraCount() const { return (uint32_t)mCameras.size(); } - const Camera::SharedPtr getCamera(uint32_t index) const { return index < getCameraCount() ? mCameras[index] : nullptr; } - const Camera::SharedPtr getActiveCamera() const { return getCamera(mActiveCameraID); } - uint32_t getActiveCameraIndex() const { return mActiveCameraID; } - - void setActiveCamera(uint32_t camID); - float getCameraSpeed() const { return mCameraSpeed; } - void setCameraSpeed(float speed) { mCameraSpeed = speed; } - - // Camera update - virtual bool update(double currentTime, CameraController* cameraController = nullptr); - - // User variables - uint32_t getVersion() const { return mVersion; } - void setVersion(uint32_t version) { mVersion = version; } - void addUserVariable(const std::string& name, const UserVariable& var) { mUserVars[name] = var; } - - // If the name is not found, returns an invalid var (Type == Unknown) - const UserVariable& getUserVariable(const std::string& name) const; - const UserVariable& getUserVariable(uint32_t varID, std::string& varName) const; - uint32_t getUserVariableCount() const { return (uint32_t)mUserVars.size(); } - - const uint32_t getId() const { return mId; } - - static const uint32_t kNoPath = (uint32_t)-1; - - void merge(const Scene* pFrom); - - /** Set scene unit. - \param[in] sceneUnit Size of scene unit in meters. - */ - void setSceneUnit(float sceneUnit) { mSceneUnit = sceneUnit; } - - /** Get scene unit. - \return Size of scene unit in meters. - */ - float getSceneUnit() const { return mSceneUnit; } - - /** - Return scene extents in scene units - */ - const vec3& getCenter() { updateExtents(); return mCenter; } - const float getRadius() { updateExtents(); return mRadius; } - - /** Returns the scene bounding box. - \return Bounding box in scene units. - */ - const BoundingBox& getBoundingBox() { updateExtents(); return mBoundingBox; } - - /** - This routine creates area light(s) in the scene. All meshes that - have emissive material are treated as area lights. - */ - void createAreaLights(); - - /** Bind a sampler to all the materials in the scene - */ - void bindSampler(Sampler::SharedPtr pSampler); - - /** Attach skinning cache to all models in scene. - */ - void attachSkinningCacheToModels(SkinningCache::SharedPtr pSkinningCache); - - /** Set an environment-map texture - */ - void setEnvironmentMap(const Texture::SharedPtr& pMap) { mpEnvMap = pMap; } - - /** Get the env-map texture - */ - const Texture::SharedPtr& getEnvironmentMap() const { return mpEnvMap; } - - /** Get the filename - */ - const std::string& getFilename() const { return mFilename; } - - /** Set a new aspect ratio for all the cameras in the scene - */ - void setCamerasAspectRatio(float ratio); - - /** Render the scene's UI - */ - void renderUI(Gui* pGui, const char* uiGroup = nullptr); - protected: - - Scene(const std::string& filename = ""); - /** - Update changed scene extents (radius and center). - */ - void updateExtents(); - - static uint32_t sSceneCounter; - - uint32_t mId; - - std::vector mModels; - std::vector mpLights; - std::vector mCameras; - std::vector mpPaths; - std::vector mpLightProbes; - std::vector mpAreaLights; - Texture::SharedPtr mpEnvMap; - - uint32_t mActiveCameraID = 0; - uint32_t mSelectedPath = 0; - float mCameraSpeed = 1; - float mLightingScale = 1.0f; - uint32_t mVersion = 1; - float mSceneUnit = 1.0f; ///< Scene unit in meters (default 1 unit = 1 m) - - float mRadius = -1.f; - vec3 mCenter = vec3(0, 0, 0); - BoundingBox mBoundingBox; ///< Scene bounding box in scene units. - - bool mExtentsDirty = true; - - std::string mFilename; - - using string_uservar_map = std::map; - string_uservar_map mUserVars; - static const UserVariable kInvalidVar; - }; - - enum_class_operators(Scene::LoadFlags); - -#define flag_str(a) case Scene::LoadFlags::a: return #a - inline std::string to_string(Scene::LoadFlags f) - { - switch (f) - { - flag_str(None); - flag_str(GenerateAreaLights); - default: - should_not_get_here(); - return ""; - } - } -#undef flag_str -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Scene/SceneExportImportCommon.h b/Framework/Source/Graphics/Scene/SceneExportImportCommon.h deleted file mode 100644 index 584a8a967..000000000 --- a/Framework/Source/Graphics/Scene/SceneExportImportCommon.h +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include - -namespace Falcor -{ - namespace SceneKeys - { - // Values only used in the importer -#ifdef SCENE_IMPORTER - static const char* kInclude = "include"; - - // Not supported in exporter yet - static const char* kLightProbes = "light_probes"; - static const char* kLightProbeRadius = "radius"; - static const char* kLightProbeDiffSamples = "diff_samples"; - static const char* kLightProbeSpecSamples = "spec_samples"; - - // Keys for values in older scene versions that are not exported anymore - static const char* kCamFovY = "fovY"; - static const char* kActivePath = "active_path"; - static const char* kAmbientIntensity = "ambient_intensity"; -#endif - - static const char* kVersion = "version"; - static const char* kSceneUnit = "scene_unit"; - static const char* kCameraSpeed = "camera_speed"; - static const char* kActiveCamera = "active_camera"; - static const char* kLightingScale = "lighting_scale"; - - static const char* kName = "name"; - static const char* kEnvMap = "env_map"; - - static const char* kModels = "models"; - static const char* kFilename = "file"; - static const char* kModelInstances = "instances"; - static const char* kTranslationVec = "translation"; - static const char* kRotationVec = "rotation"; - static const char* kScalingVec = "scaling"; - static const char* kActiveAnimation = "active_animation"; - - static const char* kCameras = "cameras"; - static const char* kCamPosition = "pos"; - static const char* kCamTarget = "target"; - static const char* kCamUp = "up"; - static const char* kCamFocalLength = "focal_length"; - static const char* kCamDepthRange = "depth_range"; - static const char* kCamAspectRatio = "aspect_ratio"; - static const char* kPathLoop = "loop"; - static const char* kPathFrames = "frames"; - static const char* kFrameTime = "time"; - - static const char* kLights = "lights"; - static const char* kType = "type"; - static const char* kDirLight = "dir_light"; - static const char* kPointLight = "point_light"; - static const char* kAreaLightRect = "area_light_rect"; - static const char* kAreaLightSphere = "area_light_sphere"; - static const char* kAreaLightDisc = "area_light_disc"; - static const char* kLightIntensity = "intensity"; - static const char* kLightOpeningAngle = "opening_angle"; - static const char* kLightPenumbraAngle = "penumbra_angle"; - static const char* kLightPos = "pos"; - static const char* kLightDirection = "direction"; - - static const char* kPaths = "paths"; - static const char* kAttachedObjects = "attached_objects"; - static const char* kModelInstance = "model_instance"; - static const char* kLight = "light"; - static const char* kCamera = "camera"; - - static const char* kMaterial = "material"; - static const char* kShadingModel = "shading_model"; - static const char* kShadingSpecGloss = "spec_gloss"; - static const char* kShadingMetalRough = "metal_rough"; - - static const char* kUserDefined = "user_defined"; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Scene/SceneExporter.cpp b/Framework/Source/Graphics/Scene/SceneExporter.cpp deleted file mode 100644 index 0c225e8b3..000000000 --- a/Framework/Source/Graphics/Scene/SceneExporter.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "rapidjson/stringbuffer.h" -#include "rapidjson/prettywriter.h" - -#include "Framework.h" -#include "SceneExporter.h" -#include -#include "Utils/Platform/OS.h" -#include "Graphics/Scene/Editor/SceneEditor.h" - -#define SCENE_EXPORTER -#include "SceneExportImportCommon.h" - - -namespace Falcor -{ - // Must be defined even though it's a const uint because value is passed as reference to functions - const uint32_t SceneExporter::kVersion; - - bool SceneExporter::saveScene(const std::string& filename, const Scene::SharedPtr& pScene, uint32_t exportOptions) - { - SceneExporter exporter(filename, pScene); - return exporter.save(exportOptions); - } - - template - void addLiteral(rapidjson::Value& jval, rapidjson::Document::AllocatorType& jallocator, const std::string& key, const T& value) - { - rapidjson::Value jkey; - jkey.SetString(key.c_str(), (uint32_t)key.size(), jallocator); - jval.AddMember(jkey, value, jallocator); - } - - void addJsonValue(rapidjson::Value& jval, rapidjson::Document::AllocatorType& jallocator, const std::string& key, rapidjson::Value& value) - { - rapidjson::Value jkey; - jkey.SetString(key.c_str(), (uint32_t)key.size(), jallocator); - jval.AddMember(jkey, value, jallocator); - } - - void addString(rapidjson::Value& jval, rapidjson::Document::AllocatorType& jallocator, const std::string& key, const std::string& value) - { - rapidjson::Value jstring, jkey; - jstring.SetString(value.c_str(), (uint32_t)value.size(), jallocator); - jkey.SetString(key.c_str(), (uint32_t)key.size(), jallocator); - - jval.AddMember(jkey, jstring, jallocator); - } - - void addBool(rapidjson::Value& jval, rapidjson::Document::AllocatorType& jallocator, const std::string& key, bool isValue) - { - rapidjson::Value jbool, jkey; - jbool.SetBool(isValue); - jkey.SetString(key.c_str(), (uint32_t)key.size(), jallocator); - - jval.AddMember(jkey, jbool, jallocator); - } - - template - void addVector(rapidjson::Value& jval, rapidjson::Document::AllocatorType& jallocator, const std::string& key, const T& value) - { - rapidjson::Value jkey; - jkey.SetString(key.c_str(), (uint32_t)key.size(), jallocator); - rapidjson::Value jvec(rapidjson::kArrayType); - for (int32_t i = 0; i < value.length(); i++) - { - // Print warning and abort on invalid floating-point value, otherwise the export fails and we get a corrupt file. - if (isnan(value[i]) || isinf(value[i])) - { - logWarning("SceneExporter: invalid number found (inf/nan), ignoring value"); - return; - } - jvec.PushBack(value[i], jallocator); - } - - jval.AddMember(jkey, jvec, jallocator); - } - - bool SceneExporter::save(uint32_t exportOptions) - { - mExportOptions = exportOptions; - - // create the file - mJDoc.SetObject(); - - // Write the version - rapidjson::Value& JVal = mJDoc; - auto& allocator = mJDoc.GetAllocator(); - addLiteral(JVal, allocator, SceneKeys::kVersion, kVersion); - - // Write everything else - bool exportPaths = (exportOptions & ExportPaths) != 0; - if (exportOptions & ExportGlobalSettings) writeGlobalSettings(exportPaths); - if (exportOptions & ExportModels) writeModels(); - if (exportOptions & ExportLights) writeLights(); - if (exportOptions & ExportCameras) writeCameras(); - if (exportOptions & ExportUserDefined) writeUserDefinedSection(); - if (exportOptions & ExportPaths) writePaths(); - - // Get the output string - rapidjson::StringBuffer buffer; - rapidjson::PrettyWriter writer(buffer); - writer.SetIndent(' ', 4); - mJDoc.Accept(writer); - std::string str(buffer.GetString(), buffer.GetSize()); - - // Output the file - std::ofstream outputStream(mFilename.c_str()); - if (outputStream.fail()) - { - logError("Can't open output scene file " + mFilename + ".\nExporting failed."); - return false; - } - outputStream << str; - outputStream.close(); - - return true; - } - - void SceneExporter::writeGlobalSettings(bool writeActivePath) - { - rapidjson::Value& jval = mJDoc; - auto& Allocator = mJDoc.GetAllocator(); - - addLiteral(jval, Allocator, SceneKeys::kSceneUnit, mpScene->getSceneUnit()); - addLiteral(jval, Allocator, SceneKeys::kCameraSpeed, mpScene->getCameraSpeed()); - addLiteral(jval, Allocator, SceneKeys::kLightingScale, mpScene->getLightingScale()); - - if (mpScene->getCameraCount() > 0) - { - addString(jval, Allocator, SceneKeys::kActiveCamera, mpScene->getActiveCamera()->getName()); - } - } - - void createModelValue(const Scene::SharedPtr& pScene, uint32_t modelID, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& jmodel) - { - jmodel.SetObject(); - - const Model* pModel = pScene->getModel(modelID).get(); - - // Export model properties - addString(jmodel, allocator, SceneKeys::kFilename, stripDataDirectories(pModel->getFilename())); - addString(jmodel, allocator, SceneKeys::kName, pModel->getName()); - - if (pScene->getModel(modelID)->hasAnimations()) - { - addLiteral(jmodel, allocator, SceneKeys::kActiveAnimation, pModel->getActiveAnimation()); - } - - // Export model material properties - rapidjson::Value materialValue; - materialValue.SetObject(); - switch (pModel->getMesh(0)->getMaterial()->getShadingModel()) - { - case ShadingModelMetalRough: - addString(materialValue, allocator, SceneKeys::kShadingModel, SceneKeys::kShadingMetalRough); - break; - case ShadingModelSpecGloss: - addString(materialValue, allocator, SceneKeys::kShadingModel, SceneKeys::kShadingSpecGloss); - break; - default: - logWarning("SceneExporter: Unknown shading model found on model " + pModel->getName() + ", ignoring value"); - } - addJsonValue(jmodel, allocator, SceneKeys::kMaterial, materialValue); - - // Export model instances - rapidjson::Value jsonInstanceArray; - jsonInstanceArray.SetArray(); - for (uint32_t i = 0; i < pScene->getModelInstanceCount(modelID); i++) - { - rapidjson::Value jsonInstance; - jsonInstance.SetObject(); - auto& pInstance = pScene->getModelInstance(modelID, i); - - addString(jsonInstance, allocator, SceneKeys::kName, pInstance->getName()); - addVector(jsonInstance, allocator, SceneKeys::kTranslationVec, pInstance->getTranslation()); - addVector(jsonInstance, allocator, SceneKeys::kScalingVec, pInstance->getScaling()); - - // Translate rotation to degrees - glm::vec3 rotation = glm::degrees(pInstance->getRotation()); - addVector(jsonInstance, allocator, SceneKeys::kRotationVec, rotation); - - jsonInstanceArray.PushBack(jsonInstance, allocator); - } - - addJsonValue(jmodel, allocator, SceneKeys::kModelInstances, jsonInstanceArray); - } - - void SceneExporter::writeModels() - { - if (mpScene->getModelCount() == 0) - { - return; - } - - rapidjson::Value jsonModelArray; - jsonModelArray.SetArray(); - - for (uint32_t i = 0; i < mpScene->getModelCount(); i++) - { - rapidjson::Value jsonModel; - createModelValue(mpScene, i, mJDoc.GetAllocator(), jsonModel); - jsonModelArray.PushBack(jsonModel, mJDoc.GetAllocator()); - } - addJsonValue(mJDoc, mJDoc.GetAllocator(), SceneKeys::kModels, jsonModelArray); - } - - void createPointLightValue(const PointLight* pLight, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& jsonLight) - { - addString(jsonLight, allocator, SceneKeys::kName, pLight->getName()); - addString(jsonLight, allocator, SceneKeys::kType, SceneKeys::kPointLight); - addVector(jsonLight, allocator, SceneKeys::kLightIntensity, pLight->getIntensity()); - addVector(jsonLight, allocator, SceneKeys::kLightPos, pLight->getWorldPosition()); - addVector(jsonLight, allocator, SceneKeys::kLightDirection, pLight->getWorldDirection()); - addLiteral(jsonLight, allocator, SceneKeys::kLightOpeningAngle, glm::degrees(pLight->getOpeningAngle())); - addLiteral(jsonLight, allocator, SceneKeys::kLightPenumbraAngle, glm::degrees(pLight->getPenumbraAngle())); - } - - void createDirectionalLightValue(const DirectionalLight* pLight, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& jsonLight) - { - addString(jsonLight, allocator, SceneKeys::kName, pLight->getName()); - addString(jsonLight, allocator, SceneKeys::kType, SceneKeys::kDirLight); - addVector(jsonLight, allocator, SceneKeys::kLightIntensity, pLight->getIntensity()); - addVector(jsonLight, allocator, SceneKeys::kLightDirection, pLight->getWorldDirection()); - } - - void createLightValue(const Scene::SharedPtr& pScene, uint32_t lightID, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& jsonLight) - { - jsonLight.SetObject(); - const auto pLight = pScene->getLight(lightID); - - switch (pLight->getType()) - { - case LightPoint: - createPointLightValue((PointLight*)pLight.get(), allocator, jsonLight); - break; - case LightDirectional: - createDirectionalLightValue((DirectionalLight*)pLight.get(), allocator, jsonLight); - break; - default: - should_not_get_here(); - break; - } - } - - void SceneExporter::writeLights() - { - if (mpScene->getLightCount() == 0) - { - return; - } - - rapidjson::Value jsonLightsArray(rapidjson::kArrayType); - - uint32_t numLightsSaved = 0; - for (uint32_t i = 0; i < mpScene->getLightCount(); i++) - { - if (mpScene->getLights()[i]->getType() != LightPoint && - mpScene->getLights()[i]->getType() != LightDirectional) - { - continue; - } - rapidjson::Value jsonLight; - createLightValue(mpScene, i, mJDoc.GetAllocator(), jsonLight); - jsonLightsArray.PushBack(jsonLight, mJDoc.GetAllocator()); - numLightsSaved++; - } - if (numLightsSaved > 0) - { - addJsonValue(mJDoc, mJDoc.GetAllocator(), SceneKeys::kLights, jsonLightsArray); - } - } - - void createCameraValue(const Scene::SharedConstPtr& pScene, uint32_t cameraID, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& jsonCamera) - { - jsonCamera.SetObject(); - const auto pCamera = pScene->getCamera(cameraID); - addString(jsonCamera, allocator, SceneKeys::kName, pCamera->getName()); - addVector(jsonCamera, allocator, SceneKeys::kCamPosition, pCamera->getPosition()); - addVector(jsonCamera, allocator, SceneKeys::kCamTarget, pCamera->getTarget()); - addVector(jsonCamera, allocator, SceneKeys::kCamUp, pCamera->getUpVector()); - addLiteral(jsonCamera, allocator, SceneKeys::kCamFocalLength, pCamera->getFocalLength()); - glm::vec2 depthRange; - depthRange[0] = pCamera->getNearPlane(); - depthRange[1] = pCamera->getFarPlane(); - addVector(jsonCamera, allocator, SceneKeys::kCamDepthRange, depthRange); - addLiteral(jsonCamera, allocator, SceneKeys::kCamAspectRatio, pCamera->getAspectRatio()); - } - - void SceneExporter::writeCameras() - { - if (mpScene->getCameraCount() == 0) - { - return; - } - - rapidjson::Value jsonCameraArray(rapidjson::kArrayType); - for (uint32_t i = 0; i < mpScene->getCameraCount(); i++) - { - rapidjson::Value jsonCamera; - createCameraValue(mpScene, i, mJDoc.GetAllocator(), jsonCamera); - jsonCameraArray.PushBack(jsonCamera, mJDoc.GetAllocator()); - } - addJsonValue(mJDoc, mJDoc.GetAllocator(), SceneKeys::kCameras, jsonCameraArray); - } - - void SceneExporter::writePaths() - { - if (mpScene->getPathCount() == 0) - { - return; - } - - auto& allocator = mJDoc.GetAllocator(); - - // Loop over the paths - rapidjson::Value jsonPathsArray(rapidjson::kArrayType); - for (uint32_t pathID = 0; pathID < mpScene->getPathCount(); pathID++) - { - const auto pPath = mpScene->getPath(pathID); - rapidjson::Value jsonPath; - jsonPath.SetObject(); - addString(jsonPath, allocator, SceneKeys::kName, pPath->getName()); - addBool(jsonPath, allocator, SceneKeys::kPathLoop, pPath->isRepeatOn()); - - // Add the keyframes - rapidjson::Value jsonFramesArray(rapidjson::kArrayType); - for (uint32_t frameID = 0; frameID < pPath->getKeyFrameCount(); frameID++) - { - const auto& frame = pPath->getKeyFrame(frameID); - rapidjson::Value jsonFrame(rapidjson::kObjectType); - addLiteral(jsonFrame, allocator, SceneKeys::kFrameTime, frame.time); - addVector(jsonFrame, allocator, SceneKeys::kCamPosition, frame.position); - addVector(jsonFrame, allocator, SceneKeys::kCamTarget, frame.target); - addVector(jsonFrame, allocator, SceneKeys::kCamUp, frame.up); - - jsonFramesArray.PushBack(jsonFrame, allocator); - } - - addJsonValue(jsonPath, allocator, SceneKeys::kPathFrames, jsonFramesArray); - - // Add attached objects - rapidjson::Value jsonObjectsArray(rapidjson::kArrayType); - for (uint32_t i = 0; i < pPath->getAttachedObjectCount(); i++) - { - rapidjson::Value jsonObject(rapidjson::kObjectType); - - const auto& pMovable = pPath->getAttachedObject(i); - - const auto& pModelInstance = std::dynamic_pointer_cast(pMovable); - const auto& pCamera = std::dynamic_pointer_cast(pMovable); - const auto& pLight = std::dynamic_pointer_cast(pMovable); - - if (pModelInstance != nullptr) - { - addString(jsonObject, allocator, SceneKeys::kType, SceneKeys::kModelInstance); - addString(jsonObject, allocator, SceneKeys::kName, pModelInstance->getName()); - } - else if (pCamera != nullptr) - { - addString(jsonObject, allocator, SceneKeys::kType, SceneKeys::kCamera); - addString(jsonObject, allocator, SceneKeys::kName, pCamera->getName()); - } - else if (pLight != nullptr) - { - addString(jsonObject, allocator, SceneKeys::kType, SceneKeys::kLight); - addString(jsonObject, allocator, SceneKeys::kName, pLight->getName()); - } - - jsonObjectsArray.PushBack(jsonObject, allocator); - } - - addJsonValue(jsonPath, allocator, SceneKeys::kAttachedObjects, jsonObjectsArray); - - // Finish path - jsonPathsArray.PushBack(jsonPath, allocator); - } - - addJsonValue(mJDoc, allocator, SceneKeys::kPaths, jsonPathsArray); - } - - void SceneExporter::writeUserDefinedSection() - { - if (mpScene->getUserVariableCount() == 0) - { - return; - } - - rapidjson::Value jsonUserValues(rapidjson::kObjectType); - auto& allocator = mJDoc.GetAllocator(); - - // TODO -- use these. unused scenekeys to avoid linux warning - (void)SceneKeys::kEnvMap; - (void)SceneKeys::kAreaLightRect; - (void)SceneKeys::kAreaLightSphere; - (void)SceneKeys::kAreaLightDisc; - - for (uint32_t varID = 0; varID < mpScene->getUserVariableCount(); varID++) - { - std::string name; - const auto& var = mpScene->getUserVariable(varID, name); - - switch (var.type) - { - case Scene::UserVariable::Type::Int: - addLiteral(jsonUserValues, allocator, name, var.i32); - break; - case Scene::UserVariable::Type::Uint: - addLiteral(jsonUserValues, allocator, name, var.u32); - break; - case Scene::UserVariable::Type::Int64: - addLiteral(jsonUserValues, allocator, name, var.i64); - break; - case Scene::UserVariable::Type::Uint64: - addLiteral(jsonUserValues, allocator, name, var.u64); - break; - case Scene::UserVariable::Type::Double: - addLiteral(jsonUserValues, allocator, name, var.d64); - break; - case Scene::UserVariable::Type::String: - addString(jsonUserValues, allocator, name, var.str); - break; - case Scene::UserVariable::Type::Vec2: - addVector(jsonUserValues, allocator, name, var.vec2); - break; - case Scene::UserVariable::Type::Vec3: - addVector(jsonUserValues, allocator, name, var.vec3); - break; - case Scene::UserVariable::Type::Vec4: - addVector(jsonUserValues, allocator, name, var.vec4); - break; - case Scene::UserVariable::Type::Bool: - addBool(jsonUserValues, allocator, name, var.b); - break; - default: - should_not_get_here(); - return; - } - } - - addJsonValue(mJDoc, allocator, SceneKeys::kUserDefined, jsonUserValues); - } -} diff --git a/Framework/Source/Graphics/Scene/SceneExporter.h b/Framework/Source/Graphics/Scene/SceneExporter.h deleted file mode 100644 index d46f40b39..000000000 --- a/Framework/Source/Graphics/Scene/SceneExporter.h +++ /dev/null @@ -1,79 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "glm/vec4.hpp" -#include "Scene.h" -#include "rapidjson/document.h" -#include - -namespace Falcor -{ - - class SceneExporter - { - public: - friend class Scene; - - enum : uint32_t - { - ExportGlobalSettings = 0x1, - ExportModels = 0x2, - ExportLights = 0x4, - ExportCameras = 0x8, - ExportPaths = 0x10, - ExportUserDefined = 0x20, - ExportAll = 0xFFFFFFFF - }; - - static bool saveScene(const std::string& filename, const Scene::SharedPtr& pScene, uint32_t exportOptions = ExportAll); - - static const uint32_t kVersion = 2; - - private: - - SceneExporter(const std::string& filename, const Scene::SharedPtr& pScene) - : mpScene(pScene), mFilename(filename) {} - - bool save(uint32_t exportOptions); - - void writeModels(); - void writeLights(); - void writeCameras(); - void writeGlobalSettings(bool writeActivePath); - void writePaths(); - void writeUserDefinedSection(); - - rapidjson::Document mJDoc; - Scene::SharedPtr mpScene = nullptr; - std::string mFilename; - uint32_t mExportOptions = 0; - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Scene/SceneImporter.h b/Framework/Source/Graphics/Scene/SceneImporter.h deleted file mode 100644 index aed46c55d..000000000 --- a/Framework/Source/Graphics/Scene/SceneImporter.h +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "rapidjson/document.h" -#include "Graphics/Material/Material.h" -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "glm/vec4.hpp" -#include "Scene.h" - -namespace Falcor -{ - class SceneImporter - { - public: - static bool loadScene(Scene& scene, const std::string& filename, Model::LoadFlags modelLoadFlags, Scene::LoadFlags sceneLoadFlags); - - private: - - SceneImporter(Scene& scene) : mScene(scene) {} - bool load(const std::string& filename, Model::LoadFlags modelLoadFlags, Scene::LoadFlags sceneLoadFlags); - - bool parseVersion(const rapidjson::Value& jsonVal); - bool parseSceneUnit(const rapidjson::Value& jsonVal); - bool parseModels(const rapidjson::Value& jsonVal); - bool parseLights(const rapidjson::Value& jsonVal); - bool parseLightProbes(const rapidjson::Value& jsonVal); - bool parseCameras(const rapidjson::Value& jsonVal); - bool parseAmbientIntensity(const rapidjson::Value& jsonVal); - bool parseActiveCamera(const rapidjson::Value& jsonVal); - bool parseCameraSpeed(const rapidjson::Value& jsonVal); - bool parseLightingScale(const rapidjson::Value& jsonVal); - bool parsePaths(const rapidjson::Value& jsonVal); - bool parseUserDefinedSection(const rapidjson::Value& jsonVal); - bool parseActivePath(const rapidjson::Value& jsonVal); - bool parseIncludes(const rapidjson::Value& jsonVal); - bool parseEnvMap(const rapidjson::Value& jsonVal); - - bool topLevelLoop(); - - bool loadIncludeFile(const std::string& Include); - - bool createModel(const rapidjson::Value& jsonModel); - bool createModelInstances(const rapidjson::Value& jsonVal, const Model::SharedPtr& pModel); - bool createPointLight(const rapidjson::Value& jsonLight); - bool createDirLight(const rapidjson::Value& jsonLight); - bool createAnalyticAreaLight(const rapidjson::Value& jsonLight); - ObjectPath::SharedPtr createPath(const rapidjson::Value& jsonPath); - bool createPathFrames(ObjectPath* pPath, const rapidjson::Value& jsonFramesArray); - bool createCamera(const rapidjson::Value& jsonCamera); - - bool error(const std::string& msg); - - template - bool getFloatVec(const rapidjson::Value& jsonVal, const std::string& desc, float vec[VecSize]); - bool getFloatVecAnySize(const rapidjson::Value& jsonVal, const std::string& desc, std::vector& vec); - rapidjson::Document mJDoc; - Scene& mScene; - std::string mFilename; - std::string mDirectory; - Model::LoadFlags mModelLoadFlags; - Scene::LoadFlags mSceneLoadFlags; - - using ObjectMap = std::map; - bool isNameDuplicate(const std::string& name, const ObjectMap& objectMap, const std::string& objectType) const; - IMovableObject::SharedPtr getMovableObject(const std::string& type, const std::string& name) const; - - ObjectMap mInstanceMap; - ObjectMap mCameraMap; - ObjectMap mLightMap; - - struct FuncValue - { - const std::string token; - decltype(&SceneImporter::parseModels) func; - }; - - static const FuncValue kFunctionTable[]; - bool validateSceneFile(); - }; -} \ No newline at end of file diff --git a/Framework/Source/Graphics/Scene/SceneRenderer.cpp b/Framework/Source/Graphics/Scene/SceneRenderer.cpp deleted file mode 100644 index 7213aa806..000000000 --- a/Framework/Source/Graphics/Scene/SceneRenderer.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "SceneRenderer.h" -#include "Graphics/Program/Program.h" -#include "Utils/Gui.h" -#include "API/ConstantBuffer.h" -#include "API/RenderContext.h" -#include "Scene.h" -#include "Utils/Platform/OS.h" -#include "VR/OpenVR/VRSystem.h" -#include "API/Device.h" -#include "glm/matrix.hpp" - -namespace Falcor -{ - size_t SceneRenderer::sBonesOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sBonesInvTransposeOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sCameraDataOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sWorldMatArraySize = 0; - size_t SceneRenderer::sWorldMatOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sPrevWorldMatOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sWorldInvTransposeMatOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sMeshIdOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sDrawIDOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sLightCountOffset = ConstantBuffer::kInvalidOffset; - size_t SceneRenderer::sLightArrayOffset = ConstantBuffer::kInvalidOffset; - - const char* SceneRenderer::kPerFrameCbName = "InternalPerFrameCB"; - const char* SceneRenderer::kPerMeshCbName = "InternalPerMeshCB"; - const char* SceneRenderer::kBoneCbName = "InternalBoneCB"; - const char* SceneRenderer::kProbeVarName = "gLightProbe"; - const char* SceneRenderer::kProbeSharedVarName = "gProbeShared"; - const char* SceneRenderer::kAreaLightCbName = "InternalAreaLightCB"; - - - SceneRenderer::SharedPtr SceneRenderer::create(const Scene::SharedPtr& pScene) - { - return SharedPtr(new SceneRenderer(pScene)); - } - - SceneRenderer::SceneRenderer(const Scene::SharedPtr& pScene) : mpScene(pScene) - { - setCameraControllerType(CameraControllerType::SixDof); - } - - void SceneRenderer::updateVariableOffsets(const ProgramReflection* pReflector) - { - const ParameterBlockReflection* pBlock = pReflector->getDefaultParameterBlock().get(); - if (sWorldMatOffset == ConstantBuffer::kInvalidOffset) - { - const ReflectionVar* pVar = pBlock->getResource(kPerMeshCbName).get(); - assert(pVar->getType()->asResourceType()->getType() == ReflectionResourceType::Type::ConstantBuffer); - - if (pVar != nullptr) - { - const ReflectionType* pType = pVar->getType().get(); - - assert(pType->findMember("gWorldMat[0]")->getType()->asBasicType()->isRowMajor() == true); // We copy into CBs as row-major - assert(pType->findMember("gWorldInvTransposeMat[0]")->getType()->asBasicType()->isRowMajor() == true); - assert(pType->findMember("gWorldMat")->getType()->getTotalArraySize() == pType->findMember("gWorldInvTransposeMat")->getType()->getTotalArraySize()); - - sWorldMatArraySize = pType->findMember("gWorldMat")->getType()->getTotalArraySize(); - sWorldMatOffset = pType->findMember("gWorldMat[0]")->getOffset(); - sWorldInvTransposeMatOffset = pType->findMember("gWorldInvTransposeMat[0]")->getOffset(); - sMeshIdOffset = pType->findMember("gMeshId")->getOffset(); - sDrawIDOffset = pType->findMember("gDrawId[0]")->getOffset(); - sPrevWorldMatOffset = pType->findMember("gPrevWorldMat[0]")->getOffset(); - } - } - - if (sCameraDataOffset == ConstantBuffer::kInvalidOffset) - { - const ReflectionVar* pVar = pBlock->getResource(kPerFrameCbName).get(); - - if (pVar != nullptr) - { - assert(pVar->getType()->asResourceType()->getType() == ReflectionResourceType::Type::ConstantBuffer); - const ReflectionType* pType = pVar->getType().get(); - sCameraDataOffset = pType->findMember("gCamera.viewMat")->getOffset(); - const auto& pCountOffset = pType->findMember("gLightsCount"); - sLightCountOffset = pCountOffset ? pCountOffset->getOffset() : ConstantBuffer::kInvalidOffset; - const auto& pLightOffset = pType->findMember("gLights"); - sLightArrayOffset = pLightOffset ? pLightOffset->getOffset() : ConstantBuffer::kInvalidOffset; - } - } - } - - void SceneRenderer::setPerFrameData(const CurrentWorkingData& currentData) - { - ConstantBuffer* pCB = currentData.pVars->getConstantBuffer(kPerFrameCbName).get(); - if (pCB) - { - // Set camera - if (currentData.pCamera) - { - currentData.pCamera->setIntoConstantBuffer(pCB, sCameraDataOffset); - } - - // Set lights - if (sLightArrayOffset != ConstantBuffer::kInvalidOffset) - { - assert(mpScene->getLightCount() <= MAX_LIGHT_SOURCES); // Max array size in the shader - for (uint32_t i = 0; i < mpScene->getLightCount(); i++) - { - mpScene->getLight(i)->setIntoProgramVars(currentData.pVars, pCB, sLightArrayOffset + (i * Light::getShaderStructSize())); - } - } - if (sLightCountOffset != ConstantBuffer::kInvalidOffset) - { - pCB->setVariable(sLightCountOffset, mpScene->getLightCount()); - } - if (mpScene->getLightProbeCount() > 0) - { - // #TODO Support multiple light probes - LightProbe::setCommonIntoProgramVars(currentData.pVars, kProbeSharedVarName); - mpScene->getLightProbe(0)->setIntoProgramVars(currentData.pVars, pCB, kProbeVarName); - } - } - - if (mpScene->getAreaLightCount() > 0) - { - const ParameterBlockReflection* pBlock = currentData.pVars->getReflection()->getDefaultParameterBlock().get(); - - // If area lights have been declared - const ReflectionVar* pVar = pBlock->getResource(kAreaLightCbName).get(); - if (pVar != nullptr) - { - const ReflectionVar* pAreaLightVar = pVar->getType()->findMember("gAreaLights").get(); - assert(pAreaLightVar != nullptr); - - uint32_t areaLightArraySize = pAreaLightVar->getType()->asArrayType()->getArraySize(); - for (uint32_t i = 0; i < min(areaLightArraySize, mpScene->getAreaLightCount()); i++) - { - std::string varName = "gAreaLights[" + std::to_string(i) + "]"; - mpScene->getAreaLight(i)->setIntoProgramVars(currentData.pVars, currentData.pVars->getConstantBuffer(kAreaLightCbName).get(), varName.c_str()); - } - } - } - } - - bool SceneRenderer::setPerModelData(const CurrentWorkingData& currentData) - { - const Model* pModel = currentData.pModel; - - // Set bones - if (pModel->hasBones()) - { - ConstantBuffer* pCB = currentData.pVars->getConstantBuffer(kBoneCbName).get(); - if (pCB != nullptr) - { - if (sBonesOffset == ConstantBuffer::kInvalidOffset || sBonesInvTransposeOffset == ConstantBuffer::kInvalidOffset) - { - sBonesOffset = pCB->getVariableOffset("gBoneMat[0]"); - sBonesInvTransposeOffset = pCB->getVariableOffset("gInvTransposeBoneMat[0]"); - } - - assert(pModel->getBoneCount() <= MAX_BONES); - pCB->setVariableArray(sBonesOffset, pModel->getBoneMatrices(), pModel->getBoneCount()); - pCB->setVariableArray(sBonesInvTransposeOffset, pModel->getBoneInvTransposeMatrices(), pModel->getBoneCount()); - } - } - return true; - } - - bool SceneRenderer::setPerModelInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, uint32_t instanceID) - { - return true; - } - - bool SceneRenderer::setPerMeshData(const CurrentWorkingData& currentData, const Mesh* pMesh) - { - return true; - } - - bool SceneRenderer::setPerMeshInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance, uint32_t drawInstanceID) - { - ConstantBuffer* pCB = currentData.pVars->getConstantBuffer(kPerMeshCbName).get(); - if (pCB) - { - const Mesh* pMesh = pMeshInstance->getObject().get(); - - glm::mat4 worldMat = pModelInstance->getTransformMatrix(); - glm::mat4 prevWorldMat = pModelInstance->getPrevTransformMatrix(); - - if (pMesh->hasBones() == false) - { - worldMat = worldMat * pMeshInstance->getTransformMatrix(); - prevWorldMat = prevWorldMat * pMeshInstance->getPrevTransformMatrix(); - } - - glm::mat3x4 worldInvTransposeMat = transpose(inverse(glm::mat3(worldMat))); - - assert(drawInstanceID < sWorldMatArraySize); - pCB->setBlob(&worldMat, sWorldMatOffset + drawInstanceID * sizeof(glm::mat4), sizeof(glm::mat4)); - pCB->setBlob(&worldInvTransposeMat, sWorldInvTransposeMatOffset + drawInstanceID * sizeof(glm::mat3x4), sizeof(glm::mat3x4)); // HLSL uses column-major and packing rules require 16B alignment, hence use glm:mat3x4 - pCB->setBlob(&prevWorldMat, sPrevWorldMatOffset + drawInstanceID * sizeof(glm::mat4), sizeof(glm::mat4)); - - // Set mesh id - pCB->setVariable(sMeshIdOffset, pMesh->getId()); - } - - return true; - } - - bool SceneRenderer::setPerMaterialData(const CurrentWorkingData& currentData, const Material* pMaterial) - { - currentData.pVars->setParameterBlock("gMaterial", pMaterial->getParameterBlock()); - return true; - } - - void SceneRenderer::executeDraw(const CurrentWorkingData& currentData, uint32_t indexCount, uint32_t instanceCount) - { - // Draw - currentData.pContext->drawIndexedInstanced(indexCount, instanceCount, 0, 0, 0); - } - - void SceneRenderer::draw(CurrentWorkingData& currentData, const Mesh* pMesh, uint32_t instanceCount) - { - currentData.pMaterial = pMesh->getMaterial().get(); - // Bind material - if(mpLastMaterial != pMesh->getMaterial().get()) - { - if (setPerMaterialData(currentData, currentData.pMaterial) == false) - { - return; - } - mpLastMaterial = pMesh->getMaterial().get(); - - if(mCompileMaterialWithProgram) - { - currentData.pState->getProgram()->addDefine("_MS_STATIC_MATERIAL_FLAGS", std::to_string(mpLastMaterial->getFlags())); - } - } - - executeDraw(currentData, pMesh->getIndexCount(), instanceCount); - postFlushDraw(currentData); - currentData.pState->getProgram()->removeDefine("_MS_STATIC_MATERIAL_FLAGS"); - } - - void SceneRenderer::postFlushDraw(const CurrentWorkingData& currentData) - { - - } - - bool SceneRenderer::cullMeshInstance(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance) - { - BoundingBox box = pMeshInstance->getBoundingBox().transform(pModelInstance->getTransformMatrix()); - return currentData.pCamera->isObjectCulled(box); - } - - void SceneRenderer::renderMeshInstances(CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, uint32_t meshID) - { - const Model* pModel = currentData.pModel; - const Mesh* pMesh = pModel->getMesh(meshID).get(); - - if (setPerMeshData(currentData, pMesh)) - { - Program* pProgram = currentData.pState->getProgram().get(); - bool useVsSkinning = pMesh->hasBones() && !pModel->getSkinningCache(); - if (useVsSkinning) - { - pProgram->addDefine("_VERTEX_BLENDING"); - } - - // Bind VAO and set topology - currentData.pState->setVao(useVsSkinning ? pMesh->getVao() : pModel->getMeshVao(pMesh)); - - uint32_t activeInstances = 0; - - const uint32_t instanceCount = pModel->getMeshInstanceCount(meshID); - for (uint32_t instanceID = 0; instanceID < instanceCount; instanceID++) - { - const Model::MeshInstance* pMeshInstance = pModel->getMeshInstance(meshID, instanceID).get(); - - if (pMeshInstance->isVisible()) - { - if ((mCullEnabled == false) || (cullMeshInstance(currentData, pModelInstance, pMeshInstance) == false)) - { - if (setPerMeshInstanceData(currentData, pModelInstance, pMeshInstance, activeInstances)) - { - currentData.drawID++; - activeInstances++; - - if (activeInstances == mMaxInstanceCount) - { - // DISABLED_FOR_D3D12 - //pContext->setProgram(currentData.pProgram->getActiveProgramVersion()); - draw(currentData, pMesh, activeInstances); - activeInstances = 0; - } - } - } - } - } - if(activeInstances != 0) - { - draw(currentData, pMesh, activeInstances); - } - - // Restore the program state - if (useVsSkinning) - { - pProgram->removeDefine("_VERTEX_BLENDING"); - } - } - } - - void SceneRenderer::renderModelInstance(CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance) - { - mpLastMaterial = nullptr; - - // Loop over the meshes - for (uint32_t meshID = 0; meshID < pModelInstance->getObject()->getMeshCount(); meshID++) - { - renderMeshInstances(currentData, pModelInstance, meshID); - } - } - - bool SceneRenderer::update(double currentTime) - { - return mpScene->update(currentTime, mpCameraController.get()); - } - - void SceneRenderer::renderScene(RenderContext* pContext) - { - renderScene(pContext, mpScene->getActiveCamera().get()); - } - - void SceneRenderer::renderScene(CurrentWorkingData& currentData) - { - setPerFrameData(currentData); - - for (uint32_t modelID = 0; modelID < mpScene->getModelCount(); modelID++) - { - currentData.pModel = mpScene->getModel(modelID).get(); - - if (setPerModelData(currentData)) - { - for (uint32_t instanceID = 0; instanceID < mpScene->getModelInstanceCount(modelID); instanceID++) - { - const auto pInstance = mpScene->getModelInstance(modelID, instanceID).get(); - if (pInstance->isVisible()) - { - if (setPerModelInstanceData(currentData, pInstance, instanceID)) - { - renderModelInstance(currentData, pInstance); - } - } - } - } - } - } - - void SceneRenderer::renderScene(RenderContext* pContext, const Camera* pCamera) - { - updateVariableOffsets(pContext->getGraphicsVars()->getReflection().get()); - - CurrentWorkingData currentData; - currentData.pContext = pContext; - currentData.pState = pContext->getGraphicsState().get(); - currentData.pVars = pContext->getGraphicsVars().get(); - currentData.pCamera = pCamera; - currentData.pMaterial = nullptr; - currentData.pModel = nullptr; - currentData.drawID = 0; - renderScene(currentData); - } - - void SceneRenderer::setCameraControllerType(CameraControllerType type) - { - switch(type) - { - case CameraControllerType::FirstPerson: - mpCameraController = CameraController::SharedPtr(new FirstPersonCameraController); - break; - case CameraControllerType::SixDof: - mpCameraController = CameraController::SharedPtr(new SixDoFCameraController); - break; - case CameraControllerType::Hmd: - mpCameraController = CameraController::SharedPtr(new HmdCameraController); - break; - default: - should_not_get_here(); - } - mCamControllerType = type; - } - - void SceneRenderer::detachCameraController() - { - mpCameraController->attachCamera(nullptr); - } - - bool SceneRenderer::onMouseEvent(const MouseEvent& mouseEvent) - { - return mpCameraController->onMouseEvent(mouseEvent); - } - - bool SceneRenderer::onKeyEvent(const KeyboardEvent& keyEvent) - { - return mpCameraController->onKeyEvent(keyEvent); - } -} diff --git a/Framework/Source/Graphics/Scene/SceneRenderer.h b/Framework/Source/Graphics/Scene/SceneRenderer.h deleted file mode 100644 index d119fe67d..000000000 --- a/Framework/Source/Graphics/Scene/SceneRenderer.h +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include "Utils/Gui.h" -#include "Graphics/Camera/CameraController.h" -#include "Graphics/Scene/Scene.h" -#include "Utils/CpuTimer.h" -#include "API/ConstantBuffer.h" -#include "Utils/DebugDrawer.h" - -namespace Falcor -{ - class Model; - class GraphicsState; - class RenderContext; - class Material; - class Mesh; - class Camera; - - class SceneRenderer - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - /** Create a renderer instance - \param[in] pScene Scene this renderer is responsible for rendering - */ - static SharedPtr create(const Scene::SharedPtr& pScene); - virtual ~SceneRenderer() = default; - - /** Renders the full scene using the scene's active camera. - Call update() before using this function, otherwise camera will not move and models will not be animated. - */ - virtual void renderScene(RenderContext* pContext); - - /** Renders the full scene, overriding the internal camera. - Call update() before using this function otherwise model animation will not work - */ - virtual void renderScene(RenderContext* pContext, const Camera* pCamera); - - /** Update the camera and model animation. - Should be called before renderScene(), unless not animations are used and you update the camera manually - */ - bool update(double currentTime); - - bool onKeyEvent(const KeyboardEvent& keyEvent); - bool onMouseEvent(const MouseEvent& mouseEvent); - - /** Enable/disable mesh culling. Culling does not always result in performance gain, especially when there are a lot of meshes to process with low rejection rate. - */ - void toggleMeshCulling(bool enable) { mCullEnabled = enable; } - - /** Check if mesh culiing is enabled - */ - bool isMeshCullingEnabled() const { return mCullEnabled; } - - /** Set the maximal number of mesh instance to dispatch in a single draw call. - */ - void setMaxInstanceCount(uint32_t instanceCount) { mMaxInstanceCount = instanceCount; } - - enum class CameraControllerType - { - FirstPerson, - SixDof, - Hmd - }; - - void setCameraControllerType(CameraControllerType type); - - void detachCameraController(); - - Scene::SharedPtr getScene() const { return mpScene; } - - void toggleStaticMaterialCompilation(bool on) { mCompileMaterialWithProgram = on; } - - protected: - - struct CurrentWorkingData - { - RenderContext* pContext = nullptr; - GraphicsVars* pVars = nullptr; - GraphicsState* pState = nullptr; - const Camera* pCamera = nullptr; - const Model* pModel = nullptr; - const Material* pMaterial = nullptr; - - uint32_t drawID; // Zero-based mesh instance draw order/ID. Resets at the beginning of renderScene, and increments per mesh instance drawn. - }; - - SceneRenderer(const Scene::SharedPtr& pScene); - Scene::SharedPtr mpScene; - - static const char* kPerFrameCbName; - static const char* kPerMeshCbName; - static const char* kBoneCbName; - static const char* kProbeVarName; - static const char* kProbeSharedVarName; - static const char* kAreaLightCbName; - - static size_t sBonesOffset; - static size_t sBonesInvTransposeOffset; - static size_t sCameraDataOffset; - static size_t sLightCountOffset; - static size_t sLightArrayOffset; - static size_t sWorldMatArraySize; - static size_t sWorldMatOffset; - static size_t sPrevWorldMatOffset; - static size_t sWorldInvTransposeMatOffset; - static size_t sMeshIdOffset; - static size_t sDrawIDOffset; - - static void updateVariableOffsets(const ProgramReflection* pReflector); - - virtual void setPerFrameData(const CurrentWorkingData& currentData); - virtual bool setPerModelData(const CurrentWorkingData& currentData); - virtual bool setPerModelInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, uint32_t instanceID); - virtual bool setPerMeshData(const CurrentWorkingData& currentData, const Mesh* pMesh); - virtual bool setPerMeshInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance, uint32_t drawInstanceID); - virtual bool setPerMaterialData(const CurrentWorkingData& currentData, const Material* pMaterial); - virtual void executeDraw(const CurrentWorkingData& currentData, uint32_t indexCount, uint32_t instanceCount); - virtual void postFlushDraw(const CurrentWorkingData& currentData); - virtual bool cullMeshInstance(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance); - - void renderModelInstance(CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance); - void renderMeshInstances(CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, uint32_t meshID); - void draw(CurrentWorkingData& currentData, const Mesh* pMesh, uint32_t instanceCount); - - void renderScene(CurrentWorkingData& currentData); - - CameraControllerType mCamControllerType = CameraControllerType::SixDof; - CameraController::SharedPtr mpCameraController; - - uint32_t mMaxInstanceCount = 64; - const Material* mpLastMaterial = nullptr; - bool mCullEnabled = true; - bool mCompileMaterialWithProgram = true; - }; -} diff --git a/Framework/Source/Sample.cpp b/Framework/Source/Sample.cpp deleted file mode 100644 index dbff7c9a7..000000000 --- a/Framework/Source/Sample.cpp +++ /dev/null @@ -1,703 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Sample.h" -#include -#include -#include "API/Window.h" -#include "Graphics/Program/Program.h" -#include "Utils/Platform/OS.h" -#include "API/FBO.h" -#include "VR/OpenVR/VRSystem.h" -#include "Utils/Platform/ProgressBar.h" -#include "Utils/StringUtils.h" -#include "Graphics/FboHelper.h" -#include -#include -#include "Experimental/RenderGraph/RenderPassLibrary.h" - -namespace Falcor -{ - static std::string kMonospaceFont = "monospace"; - - void Sample::handleWindowSizeChange() - { - if (!gpDevice) return; - // Tell the device to resize the swap chain - auto pBackBufferFBO = gpDevice->resizeSwapChain(mpWindow->getClientAreaWidth(), mpWindow->getClientAreaHeight()); - auto width = pBackBufferFBO->getWidth(); - auto height = pBackBufferFBO->getHeight(); - - // Recreate target fbo - auto pCurrentFbo = mpTargetFBO; - mpTargetFBO = FboHelper::create2D(width, height, pBackBufferFBO->getDesc()); - if(mpDefaultPipelineState) mpDefaultPipelineState->setFbo(mpTargetFBO); - gpDevice->getRenderContext()->blit(pCurrentFbo->getColorTexture(0)->getSRV(), mpTargetFBO->getRenderTargetView(0)); - - // Tell the GUI the swap-chain size changed - if(mpGui) mpGui->onWindowResize(width, height); - - // Resize the pixel zoom - if(mpPixelZoom) mpPixelZoom->onResizeSwapChain(gpDevice->getSwapChainFbo().get()); - - // Call the user callback - if(mpRenderer) mpRenderer->onResizeSwapChain(this, width, height); - } - - void Sample::handleKeyboardEvent(const KeyboardEvent& keyEvent) - { - if (keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - mPressedKeys.insert(keyEvent.key); - } - else if (keyEvent.type == KeyboardEvent::Type::KeyReleased) - { - mPressedKeys.erase(keyEvent.key); - } - - if(gpDevice) - { - // Check if the GUI consumes it - if (mpGui->onKeyboardEvent(keyEvent)) - { - return; - } - - // Checks if should toggle zoom - mpPixelZoom->onKeyboardEvent(keyEvent); - - // Consume system messages first - if (keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - if (keyEvent.mods.isShiftDown && keyEvent.key == KeyboardEvent::Key::F12) - { - initVideoCapture(); - } - else if (keyEvent.mods.isCtrlDown) - { - switch (keyEvent.key) - { - case KeyboardEvent::Key::Pause: - mFreezeRendering = !mFreezeRendering; - break; - } - } - else if (!keyEvent.mods.isAltDown && !keyEvent.mods.isCtrlDown && !keyEvent.mods.isShiftDown) - { - switch (keyEvent.key) - { - case KeyboardEvent::Key::F12: - mCaptureScreen = true; - break; -#if _PROFILING_ENABLED - case KeyboardEvent::Key::P: - gProfileEnabled = !gProfileEnabled; - break; -#endif - case KeyboardEvent::Key::V: - mVsyncOn = !mVsyncOn; - gpDevice->toggleVSync(mVsyncOn); - mFrameRate.resetClock(); - break; - case KeyboardEvent::Key::F1: - toggleText(!mShowText); - break; - case KeyboardEvent::Key::F2: - toggleUI((mShowUI == UIStatus::ShowAll)); - break; - case KeyboardEvent::Key::F5: - Program::reloadAllPrograms(); - if(mpRenderer) mpRenderer->onDataReload(this); - break; - case KeyboardEvent::Key::Escape: - if (mVideoCapture.pVideoCapture) - { - endVideoCapture(); - } - else - { - mpWindow->shutdown(); - } - break; - case KeyboardEvent::Key::Pause: - mFreezeTime = !mFreezeTime; - break; - } - } - } - } - - // If we got here, this is a user specific message - if(mpRenderer) mpRenderer->onKeyEvent(this, keyEvent); - } - - void Sample::handleDroppedFile(const std::string& filename) - { - if(mpRenderer) - { - mpRenderer->onDroppedFile(this, filename); - } - } - - void Sample::handleMouseEvent(const MouseEvent& mouseEvent) - { - if(gpDevice) - { - if (mpGui->onMouseEvent(mouseEvent)) return; - if (mpPixelZoom->onMouseEvent(mouseEvent)) return; - } - if(mpRenderer) - { - mpRenderer->onMouseEvent(this, mouseEvent); - } - } - - dlldecl void releaseSharedObjects(); - - // Sample functions - Sample::~Sample() - { - if (mVideoCapture.pVideoCapture) - { - endVideoCapture(); - } - - VRSystem::cleanup(); - - RenderPassLibrary::instance().shutdown(); - Scripting::shutdown(); - mpGui.reset(); - mpDefaultPipelineState.reset(); - mpTargetFBO.reset(); - mpTextRenderer.reset(); - mpPixelZoom.reset(); - releaseSharedObjects(); - if(gpDevice) gpDevice->cleanup(); - gpDevice.reset(); - } - - void Sample::run(const SampleConfig& config, Renderer::UniquePtr& pRenderer) - { - Sample s(pRenderer); - s.runInternal(config, config.argc, config.argv); - } - - void Sample::runInternal(const SampleConfig& config, uint32_t argc, char** argv) - { - mTimeScale = config.timeScale; - mFixedTimeDelta = config.fixedTimeDelta; - mFreezeTime = config.freezeTimeOnStartup; - mVsyncOn = config.deviceDesc.enableVsync; - - Scripting::start(); - -#if _LOG_ENABLED - Logger::initialize(); - Logger::showBoxOnError(config.showMessageBoxOnError); -#endif - - // Create the window - mpWindow = Window::create(config.windowDesc, this); - if (mpWindow == nullptr) - { - logError("Failed to create device and window"); - return; - } - - // Show the progress bar - ProgressBar::MessageList msgList = - { - { "Initializing Falcor" }, - { "Takes a while, doesn't it?" }, - { "Don't get too bored now" }, - { "Getting there" }, - { "Loading. Seriously, loading" }, - { "Are we there yet?"}, - { "NI!"} - }; - - ProgressBar::SharedPtr pBar = ProgressBar::create(msgList); - - if(is_set(config.flags, SampleConfig::Flags::DoNotCreateDevice) == false) - { - Device::Desc d = config.deviceDesc; - gpDevice = Device::create(mpWindow, config.deviceDesc); - if (gpDevice == nullptr) - { - logError("Failed to create device"); - return; - } - - // Get the default objects before calling onLoad() - auto pBackBufferFBO = gpDevice->getSwapChainFbo(); - mpTargetFBO = FboHelper::create2D(pBackBufferFBO->getWidth(), pBackBufferFBO->getHeight(), pBackBufferFBO->getDesc()); - mpDefaultPipelineState = GraphicsState::create(); - mpDefaultPipelineState->setFbo(mpTargetFBO); - auto pRenderContext = gpDevice->getRenderContext(); - pRenderContext->setGraphicsState(mpDefaultPipelineState); - - // Init the UI - initUI(); - mpPixelZoom = PixelZoom::create(mpTargetFBO.get()); - } - else - { - mShowText = false; - mShowUI = UIStatus::HideAll; - } - -#ifdef _WIN32 - // Set the icon - setWindowIcon("Framework\\Nvidia.ico", mpWindow->getApiHandle()); - - if (argc == 0 || argv == nullptr) - { - mArgList.parseCommandLine(GetCommandLineA()); - } - else -#endif - { - mArgList.parseCommandLine(concatCommandLine(argc, argv)); - } - - // set fixed time delta if it is provided in the command line - if (mArgList.argExists("fixedtimedelta")) mFixedTimeDelta = mArgList["fixedtimedelta"].asFloat(); - - // Load and run - mpRenderer->onLoad(this, getRenderContext()); - initializeTesting(); - pBar = nullptr; - - mFrameRate.resetClock(); - mpWindow->msgLoop(); - - if (mTestingFrames.size()) { onTestShutdown(); } - mpRenderer->onShutdown(this); - if (gpDevice) gpDevice->flushAndSync(); - mpRenderer = nullptr; - Logger::shutdown(); - } - - void Sample::calculateTime() - { - if (mFixedTimeDelta > 0.0f) - { - mCurrentTime += mFixedTimeDelta * mTimeScale; - } - else if (mFreezeTime == false) - { - float elapsedTime = mFrameRate.getLastFrameTime() * mTimeScale; - mCurrentTime += elapsedTime; - } - } - - void Sample::setDefaultGuiSize(uint32_t width, uint32_t height) - { - mSampleGuiWidth = width; - mSampleGuiHeight = height; - } - - void Sample::setDefaultGuiPosition(uint32_t x, uint32_t y) - { - mSampleGuiPositionX = x; - mSampleGuiPositionY = y; - } - - void Sample::renderGUI() - { - if((mShowUI != UIStatus::HideAll) || gProfileEnabled) - { - mpGui->beginFrame(); - - constexpr char help[] = - " 'F1' - Show\\Hide text\n" - " 'F2' - Show\\Hide GUI\n" - " 'F5' - Reload shaders\n" - " 'ESC' - Quit\n" - " 'V' - Toggle VSync\n" - " 'F12' - Capture screenshot\n" - " 'Shift+F12' - Video capture\n" - " 'Pause' - Pause\\resume timer\n" - " 'Ctrl+Pause' - Pause\\resume the renderer\n" - " 'Z' - Zoom in on a pixel\n" - " 'MouseWheel' - Change level of zoom\n" -#if _PROFILING_ENABLED - " 'P' - Enable profiling\n"; -#else - ; -#endif - - if(mShowUI == UIStatus::ShowAll) - { - mpGui->pushWindow("Falcor", mSampleGuiWidth, mSampleGuiHeight, mSampleGuiPositionX, mSampleGuiPositionY, false, true, true, false); - mpGui->addText("Keyboard Shortcuts"); - mpGui->addTooltip(help, true); - - if (mpGui->beginGroup("Global Controls")) - { - mpGui->addFloatVar("Time", mCurrentTime, 0, FLT_MAX); - - if (mpGui->addButton("Reset")) - { - mCurrentTime = 0.0f; - } - - if (mpGui->addButton(mFreezeTime ? "Play" : "Pause", true)) - { - mFreezeTime = !mFreezeTime; - } - - if (mpGui->addButton("Stop", true)) - { - mFreezeTime = true; - mCurrentTime = 0.0f; - } - if(mpGui->beginGroup("Time Controls")) - { - mpGui->addFloatVar("Scale", mTimeScale, 0, FLT_MAX); - - if (mVideoCapture.pVideoCapture == nullptr) - { - mpGui->addFloatVar("Fixed Delta", mFixedTimeDelta, 0, FLT_MAX); - } - mpGui->endGroup(); - } - mpGui->addSeparator(); - - if (mpGui->addButton(mFreezeRendering ? "Resume Rendering" : "Pause Rendering")) mFreezeRendering = !mFreezeRendering; - mpGui->addTooltip("Freeze the renderer and keep displaying The last rendered frame. The renderer will keep accepting mouse/keyboard/GUI messages. Changes in the UI will not be reflected in the displayed image until the renderer is unfrozen"); - - mpGui->addSeparator(); - - mCaptureScreen = mpGui->addButton("Screen Capture"); - if (mpGui->addButton("Video Capture", true)) - { - initVideoCapture(); - } - - mpGui->endGroup(); - } - - mpRenderer->onGuiRender(this, mpGui.get()); - mpGui->popWindow(); - - if (mVideoCapture.displayUI && mVideoCapture.pUI) - { - mVideoCapture.pUI->render(mpGui.get()); - } - } - - if (mShowUI == UIStatus::HideGlobal) - { - mpRenderer->onGuiRender(this, mpGui.get()); - } - - if (gProfileEnabled) - { - uint32_t y = gpDevice->getSwapChainFbo()->getHeight() - 360; - - mpGui->setActiveFont(kMonospaceFont); - mpGui->pushWindow("Profiler", 650, 350, 10, y); - // Stop the timer - Profiler::endEvent("renderGUI"); - mpGui->addText(Profiler::getEventsString().c_str()); - Profiler::startEvent("renderGUI"); - mpGui->popWindow(); - mpGui->setActiveFont(""); - } - - mpGui->render(getRenderContext(), mFrameRate.getLastFrameTime()); - } - } - - bool Sample::initializeTesting() - { - bool testFrames = mArgList.argExists("testFrames"); - if (testFrames) - { - auto testFrames = mArgList.getValues("testFrames"); - for (auto frame : testFrames) { mTestingFrames.push_back(frame.asUint64()); } - - mpRenderer->onInitializeTesting(this); - } - if (mArgList.argExists("shutdownframe")) mShutdownFrame = mArgList["shutdownframe"].asUint64(); - else if(!testFrames) return false; - - return true; - } - - void Sample::onTestFrame() - { - uint64_t currentFrameID = getFrameID(); - if (mCurrentTestingIndex < mTestingFrames.size() && currentFrameID == mTestingFrames[mCurrentTestingIndex]) - { - mpRenderer->onTestFrame(this); - mCurrentTestingIndex++; - } - else if (currentFrameID == mShutdownFrame) { shutdown(); } - } - - void Sample::renderFrame() - { - if (gpDevice && gpDevice->isWindowOccluded()) - { - return; - } - - mFrameRate.newFrame(); - { - PROFILE("onFrameRender"); - calculateTime(); - // The swap-chain FBO might have changed between frames, so get it - if (!mFreezeRendering) - { - RenderContext* pRenderContext = nullptr; - if (gpDevice) - { - // Bind the default state - pRenderContext = gpDevice->getRenderContext(); - mpDefaultPipelineState->setFbo(mpTargetFBO); - pRenderContext->setGraphicsState(mpDefaultPipelineState); - } - mpRenderer->onFrameRender(this, pRenderContext, mpTargetFBO); - } - } - - if (gpDevice) - { - // Copy the render-target - const auto& pSwapChainFbo = gpDevice->getSwapChainFbo(); - RenderContext* pCtx = getRenderContext(); - getRenderContext()->copyResource(pSwapChainFbo->getColorTexture(0).get(), mpTargetFBO->getColorTexture(0).get()); - - if (mTestingFrames.size()) onTestFrame(); - - // Capture video frame before UI is rendered - bool captureVideoUI = mVideoCapture.pUI && mVideoCapture.pUI->captureUI(); // Check capture mode here once only, as its value may change after renderGUI() - if (!captureVideoUI) - { - captureVideoFrame(); - } - - //Swaps back to backbuffer to render fps text and gui directly onto it - mpDefaultPipelineState->setFbo(pSwapChainFbo); - pCtx->setGraphicsState(mpDefaultPipelineState); - { - PROFILE("renderGUI"); - renderGUI(); - } - - renderText(getFpsMsg(), glm::vec2(10, 10)); - if (mpPixelZoom) - { - mpPixelZoom->render(pCtx, pSwapChainFbo.get()); - } - -#if _PROFILING_ENABLED - Profiler::endFrame(); -#endif - // Capture video frame after UI is rendered - if (captureVideoUI) - { - captureVideoFrame(); - } - - if (mCaptureScreen) - { - captureScreen(); - } - - { - PROFILE("present"); - gpDevice->present(); - } - } - } - - std::string Sample::captureScreen(const std::string explicitFilename, const std::string explicitOutputDirectory) - { - mCaptureScreen = false; - - std::string filename = explicitFilename != "" ? explicitFilename : getExecutableName(); - std::string outputDirectory = explicitOutputDirectory != "" ? explicitOutputDirectory : getExecutableDirectory(); - - std::string pngFile; - if (findAvailableFilename(filename, outputDirectory, "png", pngFile)) - { - Texture::SharedPtr pTexture; - pTexture = gpDevice->getSwapChainFbo()->getColorTexture(0); - pTexture->captureToFile(0, 0, pngFile); - } - else - { - logError("Could not find available filename when capturing screen"); - return ""; - } - - return pngFile; - } - - void Sample::initUI() - { - float scaling = getDisplayScaleFactor(); - const auto& pSwapChainFbo = gpDevice->getSwapChainFbo(); - mpGui = Gui::create(uint32_t(pSwapChainFbo->getWidth()), uint32_t(pSwapChainFbo->getHeight()), scaling); - mpGui->addFont(kMonospaceFont, "Framework/Fonts/consolab.ttf"); - mSampleGuiHeight = (uint32_t)(mSampleGuiHeight * scaling); - mSampleGuiWidth = (uint32_t)(mSampleGuiWidth * scaling); - mpTextRenderer = TextRenderer::create(); - } - - std::string Sample::getFpsMsg() - { - std::string s; - if (mShowText) - { - std::stringstream strstr; - float msPerFrame = mFrameRate.getAverageFrameTime(); - std::string msStr = std::to_string(msPerFrame); - s = std::to_string(int(ceil(1000 / msPerFrame))) + " FPS (" + msStr.erase(msStr.size() - 4) + " ms/frame)"; - if (mVsyncOn) s += std::string(", VSync"); - } - return s; - } - - void Sample::resizeSwapChain(uint32_t width, uint32_t height) - { - mpWindow->resize(width, height); - } - - bool Sample::isKeyPressed(const KeyboardEvent::Key& key) - { - return mPressedKeys.find(key) != mPressedKeys.cend(); - } - - void Sample::renderText(const std::string& msg, const glm::vec2& position, const glm::vec2 shadowOffset) - { - if (mShowText) - { - RenderContext* pCtx = getRenderContext(); - // Render outline first - if (shadowOffset.x != 0.f || shadowOffset.y != 0) - { - const glm::vec3 oldColor = mpTextRenderer->getTextColor(); - mpTextRenderer->setTextColor(glm::vec3(0.f)); // Black outline - mpTextRenderer->begin(pCtx, position + shadowOffset); - mpTextRenderer->renderLine(pCtx, msg); - mpTextRenderer->end(pCtx); - mpTextRenderer->setTextColor(oldColor); - } - mpTextRenderer->begin(pCtx, position); - mpTextRenderer->renderLine(pCtx, msg); - mpTextRenderer->end(pCtx); - } - } - - void Sample::initVideoCapture() - { - if (mVideoCapture.pUI == nullptr) - { - mVideoCapture.pUI = VideoEncoderUI::create(20, 300, 300, 280, [this]() {startVideoCapture(); }, [this]() {endVideoCapture(); }); - } - mVideoCapture.displayUI = true; - } - - void Sample::startVideoCapture() - { - // Create the Capture Object and Framebuffer. - VideoEncoder::Desc desc; - desc.flipY = false; - desc.codec = mVideoCapture.pUI->getCodec(); - desc.filename = mVideoCapture.pUI->getFilename(); - const auto& pSwapChainFbo = gpDevice->getSwapChainFbo(); - desc.format = pSwapChainFbo->getColorTexture(0)->getFormat(); - desc.fps = mVideoCapture.pUI->getFPS(); - desc.height = pSwapChainFbo->getHeight(); - desc.width = pSwapChainFbo->getWidth(); - desc.bitrateMbps = mVideoCapture.pUI->getBitrate(); - desc.gopSize = mVideoCapture.pUI->getGopSize(); - - mVideoCapture.pVideoCapture = VideoEncoder::create(desc); - - assert(mVideoCapture.pVideoCapture); - mVideoCapture.pFrame = new uint8_t[desc.width*desc.height * 4]; - - mVideoCapture.sampleTimeDelta = mFixedTimeDelta; - mFixedTimeDelta = 1.0f / (float)desc.fps; - - if (mVideoCapture.pUI->useTimeRange()) - { - if (mVideoCapture.pUI->getStartTime() > mVideoCapture.pUI->getEndTime()) - { - mFixedTimeDelta = -mFixedTimeDelta; - } - mCurrentTime = mVideoCapture.pUI->getStartTime(); - } - - mShouldResetRendering = mVideoCapture.pUI->resetOnFirstFrame(); - } - - void Sample::endVideoCapture() - { - if (mVideoCapture.pVideoCapture) - { - mVideoCapture.pVideoCapture->endCapture(); - mShowUI = UIStatus::ShowAll; - } - mVideoCapture.pUI->setCaptureState(false); - mVideoCapture.displayUI = false; - mVideoCapture.pVideoCapture = nullptr; - safe_delete_array(mVideoCapture.pFrame); - mFixedTimeDelta = mVideoCapture.sampleTimeDelta; - } - - void Sample::captureVideoFrame() - { - if (mVideoCapture.pVideoCapture) - { - mVideoCapture.pVideoCapture->appendFrame(getRenderContext()->readTextureSubresource(gpDevice->getSwapChainFbo()->getColorTexture(0).get(), 0).data()); - - if (mVideoCapture.pUI->useTimeRange()) - { - if (mFixedTimeDelta >= 0) - { - if (mCurrentTime >= mVideoCapture.pUI->getEndTime()) - { - endVideoCapture(); - } - } - else if (mCurrentTime < mVideoCapture.pUI->getEndTime()) - { - endVideoCapture(); - } - } - - mShouldResetRendering = false; - } - } -} diff --git a/Framework/Source/Sample.h b/Framework/Source/Sample.h deleted file mode 100644 index ca542607d..000000000 --- a/Framework/Source/Sample.h +++ /dev/null @@ -1,203 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include -#include "API/Window.h" -#include "Utils/FrameRate.h" -#include "Utils/Gui.h" -#include "Utils/TextRenderer.h" -#include "API/RenderContext.h" -#include "Utils/Video/VideoEncoderUI.h" -#include "API/Device.h" -#include "ArgList.h" -#include "Utils/PixelZoom.h" -#include "Renderer.h" - -namespace Falcor -{ - /** Sample configuration. - */ - struct SampleConfig - { - /** Flags to control different sample controls - */ - enum class Flags - { - None = 0x0, ///< No flags - DoNotCreateDevice = 0x1, ///< Do not create a device. Services that depends on the device - such as GUI text - will be disabled. Use this only if you are writing raw-API sample - }; - - Window::Desc windowDesc; ///< Controls window creation - Device::Desc deviceDesc; ///< Controls device creation; - bool showMessageBoxOnError = true; ///< Show message box on framework/API errors. - float timeScale = 1.0f; ///< A scaling factor for the time elapsed between frames. - float fixedTimeDelta = 0.0f; ///< If non-zero, specifies a fixed simulation time step per frame, which is further affected by time scale. - bool freezeTimeOnStartup = false; ///< Control whether or not to start the clock when the sample start running. - Flags flags = Flags::None; ///< Sample flags - uint32_t argc = 0; ///< Arg count - char** argv = nullptr; ///< Arg values - }; - - /** Bootstrapper class for Falcor - Call Sample::run() to start the sample. - The render loop will then call the user's Renderer object - */ - class Sample : public Window::ICallbacks, public SampleCallbacks - { - public: - /** Entry-point to Sample. User should call this to start processing. - On Windows, command line args will be retrieved and parsed even if not passed through this function. - On Linux, this function is the only way to feed the sample command line args. - - \param[in] config Requested sample configuration - \param[in] pRenderer The user's renderer - \param[in] argc Optional. Number of command line arguments - \param[in] argv Optional. Array of command line arguments - */ - static void run(const SampleConfig& config, Renderer::UniquePtr& pRenderer); - - virtual ~Sample(); - protected: - /************************************************************************/ - /* Callback inherited from SampleCallbacks */ - /************************************************************************/ - RenderContext* getRenderContext() override { return gpDevice ? gpDevice->getRenderContext() : nullptr; } - Fbo::SharedPtr getCurrentFbo() override { return mpTargetFBO; } - Window* getWindow() override { return mpWindow.get(); } - Gui* getGui() override { return mpGui.get(); } - float getCurrentTime() override { return mCurrentTime; } - void resizeSwapChain(uint32_t width, uint32_t height) override; - bool isKeyPressed(const KeyboardEvent::Key& key) override; - float getFrameRate() override { return mFrameRate.getAverageFrameTime(); } - float getLastFrameTime() override { return mFrameRate.getLastFrameTime(); } - uint64_t getFrameID() override { return mFrameRate.getFrameCount(); } - void renderText(const std::string& str, const glm::vec2& position, glm::vec2 shadowOffset = vec2(1)) override; - std::string getFpsMsg() override; - void toggleText(bool showText) override { mShowText = showText && gpDevice; } - void toggleUI(bool showUI) override { if (!gpDevice || showUI) mShowUI = UIStatus::HideAll; else mShowUI = UIStatus::ShowAll; } - void toggleGlobalUI(bool showGlobalUI) override { if (!gpDevice || !showGlobalUI) mShowUI = UIStatus::HideGlobal; else mShowUI = UIStatus::ShowAll; } - void setDefaultGuiSize(uint32_t width, uint32_t height) override; - void setDefaultGuiPosition(uint32_t x, uint32_t y) override; - void setCurrentTime(float time) override { mCurrentTime = time; } - ArgList getArgList() override { return mArgList; } - void setFixedTimeDelta(float newFixedTimeDelta) override { mFixedTimeDelta = newFixedTimeDelta; } - float getFixedTimeDelta() override { return mFixedTimeDelta; } - void freezeTime(bool timeFrozen) override { mFreezeTime = timeFrozen; } - bool isTimeFrozen() override { return mFreezeTime; } - bool shouldResetRendering() override { return mShouldResetRendering; } - std::string captureScreen(const std::string explicitFilename = "", const std::string explicitOutputDirectory = "") override; - void shutdown() override { if (mpWindow) { mpWindow->shutdown(); } } - - //Non inherited testing functions - bool initializeTesting(); - void onTestFrame(); - //Any cleanup required by renderer if its being shut down early via testing - void onTestShutdown() { mpRenderer->onTestShutdown(this); } - - /** Internal data structures - */ - Gui::UniquePtr mpGui; ///< Main sample GUI - GraphicsState::SharedPtr mpDefaultPipelineState; ///< The default pipeline - Fbo::SharedPtr mpTargetFBO; ///< The FBO available to renderers - bool mFreezeTime; ///< Whether global time is frozen - bool mFreezeRendering = false; ///< Freezes the renderer - float mCurrentTime = 0; ///< Global time - float mTimeScale; ///< Global time scale - ArgList mArgList; ///< Arguments passed in by command line - Window::SharedPtr mpWindow; ///< The application's window - - void renderFrame() override; - void handleWindowSizeChange() override; - void handleKeyboardEvent(const KeyboardEvent& keyEvent) override; - void handleMouseEvent(const MouseEvent& mouseEvent) override; - void handleDroppedFile(const std::string& filename) override; - - virtual float getTimeScale() final { return mTimeScale; } - void initVideoCapture(); - - // Private functions - void initUI(); - void calculateTime(); - - void startVideoCapture(); - void endVideoCapture(); - void captureVideoFrame(); - void renderGUI(); - - void runInternal(const SampleConfig& config, uint32_t argc, char** argv); - - bool mVsyncOn = false; - bool mShowText = true; - enum class UIStatus - { - HideAll = 0, - HideGlobal, - ShowAll - }; - UIStatus mShowUI = UIStatus::ShowAll; - bool mCaptureScreen = false; - - Renderer::UniquePtr mpRenderer; - - struct VideoCaptureData - { - VideoEncoderUI::UniquePtr pUI; - VideoEncoder::UniquePtr pVideoCapture; - uint8_t* pFrame = nullptr; - float sampleTimeDelta; // Saves the sample's fixed time delta because video capture overwrites it while recording - bool displayUI = false; - }; - - VideoCaptureData mVideoCapture; - bool mShouldResetRendering = false; ///< Flag to indicate if app should reset temporally accumulated data at start of video capture (UI option). - - FrameRate mFrameRate; - - float mFixedTimeDelta; - - TextRenderer::UniquePtr mpTextRenderer; - std::set mPressedKeys; - PixelZoom::SharedPtr mpPixelZoom; - uint32_t mSampleGuiWidth = 250; - uint32_t mSampleGuiHeight = 200; - uint32_t mSampleGuiPositionX = 20; - uint32_t mSampleGuiPositionY = 40; - - // testing - std::vector mTestingFrames; - uint32_t mCurrentTestingIndex = 0; - uint64_t mShutdownFrame = static_cast(-1); - - Sample(Renderer::UniquePtr& pRenderer) : mpRenderer(std::move(pRenderer)) {} - Sample(const Sample&) = delete; - Sample& operator=(const Sample&) = delete; - }; - enum_class_operators(SampleConfig::Flags); -}; diff --git a/Framework/Source/ShadingUtils/Raytracing.slang b/Framework/Source/ShadingUtils/Raytracing.slang deleted file mode 100644 index 7f3937667..000000000 --- a/Framework/Source/ShadingUtils/Raytracing.slang +++ /dev/null @@ -1,179 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#ifndef __RAYTRACING_H__ -#define __RAYTRACING_H__ - -__exported __import Shading; -__exported __import DefaultVS; - -ByteAddressBuffer gIndices : register(t50); -ByteAddressBuffer gTexCrds : register(t51); -ByteAddressBuffer gNormals : register(t52); -ByteAddressBuffer gBitangents : register(t53); -ByteAddressBuffer gPositions : register(t54); -ByteAddressBuffer gPrevPositions : register(t55); -ByteAddressBuffer gLightMapUVs : register(t56); -shared RaytracingAccelerationStructure gRtScene : register(t57); - -// If defined, hit position is computed by barycentric interpolation of the vertex positions. -// Otherwise it is computed based on the ray equation in world space: p=o+t*d, which is numerically unstable. -// Unfortunately, interpolating the position incurs the extra cost of fetching 3x12B positions and one matrix multiply. -#define USE_INTERPOLATED_POSITION - -shared cbuffer DxrPerFrame : register(b13) -{ - uint hitProgramCount; -}; - -uint3 getIndices(uint triangleIndex) -{ - uint baseIndex = triangleIndex * 3; - int address = baseIndex * 4; - return gIndices.Load3(address); -} - -VertexOut getVertexAttributes(uint triangleIndex, float3 barycentrics) -{ - uint3 indices = getIndices(triangleIndex); - VertexOut v; - v.texC = 0; - v.normalW = 0; - v.bitangentW = 0; -#ifdef USE_INTERPOLATED_POSITION - v.posW = 0; -#else - v.posW = WorldRayOrigin() + WorldRayDirection() * RayTCurrent(); -#endif - v.colorV = 0; - v.prevPosH = 0; - v.lightmapC = 0; - - [unroll] - for (int i = 0; i < 3; i++) - { - int address = (indices[i] * 3) * 4; - v.texC += asfloat(gTexCrds.Load2(address)) * barycentrics[i]; - v.normalW += asfloat(gNormals.Load3(address)) * barycentrics[i]; - v.bitangentW += asfloat(gBitangents.Load3(address)) * barycentrics[i]; - v.lightmapC += asfloat(gLightMapUVs.Load2(address)) * barycentrics[i]; -#ifdef USE_INTERPOLATED_POSITION - v.posW += asfloat(gPositions.Load3(address)) * barycentrics[i]; -#endif - } -#ifdef USE_INTERPOLATED_POSITION - v.posW = mul(float4(v.posW, 1.f), gWorldMat[0]).xyz; -#endif -#ifndef _MS_DISABLE_INSTANCE_TRANSFORM - // Transform normal/bitangent to world space - v.normalW = mul(v.normalW, (float3x3)gWorldInvTransposeMat[0]).xyz; - v.bitangentW = mul(v.bitangentW, (float3x3)gWorldMat[0]).xyz; -#endif - v.normalW = normalize(v.normalW); - v.bitangentW = normalize(v.bitangentW); - return v; -} - -VertexOut getVertexAttributes(uint triangleIndex, BuiltInTriangleIntersectionAttributes attribs) -{ - float3 barycentrics = float3(1.0 - attribs.barycentrics.x - attribs.barycentrics.y, attribs.barycentrics.x, attribs.barycentrics.y); - return getVertexAttributes(triangleIndex, barycentrics); -} - -float3 getGeoNormal(const float3 e[2]) -{ - return normalize(cross(e[0], e[1])); -} - -/** Returns a triangle's vertex normals and edges in object space. - \param[in] triangleIndex Index of the triangle. - \param[out] n Vertex normals in object space. - \param[out] e Triangle edges in object space. -*/ -void getTriNormalsAndEdgesInObjectSpace(uint triangleIndex, out float3 n[3], out float3 e[2]) -{ - uint3 indices = getIndices(triangleIndex); - - float3 p[3]; - p[0] = asfloat(gPositions.Load3((indices[0] * 3) * 4)); - p[1] = asfloat(gPositions.Load3((indices[1] * 3) * 4)); - p[2] = asfloat(gPositions.Load3((indices[2] * 3) * 4)); - - e[0] = p[1] - p[0]; - e[1] = p[2] - p[0]; - - n[0] = asfloat(gNormals.Load3((indices[0] * 3) * 4)); - n[1] = asfloat(gNormals.Load3((indices[1] * 3) * 4)); - n[2] = asfloat(gNormals.Load3((indices[2] * 3) * 4)); -} - -/** Returns a triangle's geometric normal in world space. - \param[in] triangleIndex Index of the triangle. - \return Geometric normal in world space. Front facing for counter-clockwise winding. -*/ -float3 getGeometricNormalW(uint triangleIndex) -{ - uint3 indices = getIndices(triangleIndex); - - float3 p[3]; - p[0] = asfloat(gPositions.Load3((indices[0] * 3) * 4)); - p[1] = asfloat(gPositions.Load3((indices[1] * 3) * 4)); - p[2] = asfloat(gPositions.Load3((indices[2] * 3) * 4)); - - float3 e[2]; - e[0] = p[1] - p[0]; - e[1] = p[2] - p[0]; - - float3 N = getGeoNormal(e); - return mul(N, (float3x3)gWorldInvTransposeMat[0]).xyz; -} - -/** Returns position on triangle in the previous frame in world space. -*/ -float3 getPrevPosW(uint triangleIndex, float3 barycentrics) -{ - uint3 indices = getIndices(triangleIndex); - float3 prevPos = float3(0, 0, 0); - - [unroll] - for (int i = 0; i < 3; i++) - { - // Load vertex in object space from vertex buffer for previous frame if it exists, otherwise from the current frame. - int address = (indices[i] * 3) * 4; - prevPos += asfloat(gPrevPositions.Load3(address)) * barycentrics[i]; - } - - return mul(float4(prevPos, 1.f), gPrevWorldMat[0]).xyz; -} - -float3 getPrevPosW(uint triangleIndex, BuiltInTriangleIntersectionAttributes attribs) -{ - float3 barycentrics = float3(1.0 - attribs.barycentrics.x - attribs.barycentrics.y, attribs.barycentrics.x, attribs.barycentrics.y); - return getPrevPosW(triangleIndex, barycentrics); -} - -#endif // __RAYTRACING_H__ \ No newline at end of file diff --git a/Framework/Source/ShadingUtils/ShadingUtilsTests.cpp b/Framework/Source/ShadingUtils/ShadingUtilsTests.cpp deleted file mode 100644 index 9f1eec839..000000000 --- a/Framework/Source/ShadingUtils/ShadingUtilsTests.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "UnitTest.h" - -namespace Falcor -{ - - // Just check the first four values. - GPU_TEST(RadicalInverse) - { - ctx.createProgram("ShadingUtilsTests.cs.hlsl", "testRadicalInverse"); - ctx.allocateStructuredBuffer("result", 4); - ctx["TestCB"]["resultSize"] = 4; - ctx.runProgram(); - - const float *s = ctx.mapBuffer("result"); - EXPECT_EQ(s[0], 0.f); - EXPECT_EQ(s[1], 0.5f); - EXPECT_EQ(s[2], 0.25f); - EXPECT_EQ(s[3], 0.75f); - ctx.unmapBuffer("result"); - } - - GPU_TEST(Random) - { - ctx.createProgram("ShadingUtilsTests.cs.hlsl", "testRand"); - const int32_t n = 4 * 1024 * 1024; - ctx.allocateStructuredBuffer("result", n); - ctx["TestCB"]["resultSize"] = n; - ctx.runProgram(); - - // A fairly crude test: bucket the range [0,1] into nBuckets buckets - // and make sure that all of them have more or less 1/nBuckets of the - // total values. This doesn't really test the quality of the PRNG very - // well, but will at least detect if it's totally borked. - const float* r = ctx.mapBuffer("result"); - constexpr int32_t nBuckets = 64; - int32_t counts[nBuckets] = { 0 }; - for (int32_t i = 0; i < n; ++i) - { - EXPECT(r[i] >= 0 && r[i] < 1.f) << r[i]; - ++counts[int32_t(r[i] * nBuckets)]; - } - ctx.unmapBuffer("result"); - - for (int32_t i = 0; i < nBuckets; ++i) - { - EXPECT_GT(counts[i], .98 * n / nBuckets); - EXPECT_LT(counts[i], 1.02 * n / nBuckets); - } - } - - GPU_TEST(SphericalCoordinates) - { - ctx.createProgram("ShadingUtilsTests.cs.hlsl", "testSphericalCoordinates"); - constexpr int32_t n = 1024 * 1024; - ctx.allocateStructuredBuffer("result", n); - ctx["TestCB"]["resultSize"] = n; - // The shader runs threadgroups of 1024 threads. - ctx.runProgram(n); - - // The shader generates a bunch of random vectors, converts them to - // spherical coordinates and back, and computes the dot product with - // the original vector. Here, we'll check that the dot product is - // pretty close to one. - const float* r = ctx.mapBuffer("result"); - for (int32_t i = 0; i < n; ++i) - { - EXPECT_GT(r[i], .999f) << "i = " << i; - EXPECT_LT(r[i], 1.001f) << "i = " << i; - } - ctx.unmapBuffer("result"); - } - -} // namespace Falcor diff --git a/Framework/Source/Utils/Gui.cpp b/Framework/Source/Utils/Gui.cpp deleted file mode 100644 index c13953d60..000000000 --- a/Framework/Source/Utils/Gui.cpp +++ /dev/null @@ -1,1043 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Gui.h" -#include -#include "Utils/Platform/OS.h" -#include "Utils/UserInput.h" -#include "API/RenderContext.h" -#include "Externals/dear_imgui/imgui.h" -#include "Utils/Math/FalcorMath.h" -#include "glm/gtc/type_ptr.hpp" -#include "Utils/StringUtils.h" - -#pragma warning (disable : 4756) // overflow in constant arithmetic caused by calculating the setFloat*() functions (when calculating the step and min/max are +/- INF) -namespace Falcor -{ - void Gui::init(float scaleFactor) - { - mScaleFactor = scaleFactor; - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = (uint32_t)KeyboardEvent::Key::Tab; - io.KeyMap[ImGuiKey_LeftArrow] = (uint32_t)KeyboardEvent::Key::Left; - io.KeyMap[ImGuiKey_RightArrow] = (uint32_t)KeyboardEvent::Key::Right; - io.KeyMap[ImGuiKey_UpArrow] = (uint32_t)KeyboardEvent::Key::Up; - io.KeyMap[ImGuiKey_DownArrow] = (uint32_t)KeyboardEvent::Key::Down; - io.KeyMap[ImGuiKey_PageUp] = (uint32_t)KeyboardEvent::Key::PageUp; - io.KeyMap[ImGuiKey_PageDown] = (uint32_t)KeyboardEvent::Key::PageDown; - io.KeyMap[ImGuiKey_Home] = (uint32_t)KeyboardEvent::Key::Home; - io.KeyMap[ImGuiKey_End] = (uint32_t)KeyboardEvent::Key::End; - io.KeyMap[ImGuiKey_Delete] = (uint32_t)KeyboardEvent::Key::Del; - io.KeyMap[ImGuiKey_Backspace] = (uint32_t)KeyboardEvent::Key::Backspace; - io.KeyMap[ImGuiKey_Enter] = (uint32_t)KeyboardEvent::Key::Enter; - io.KeyMap[ImGuiKey_Escape] = (uint32_t)KeyboardEvent::Key::Escape; - io.KeyMap[ImGuiKey_A] = (uint32_t)KeyboardEvent::Key::A; - io.KeyMap[ImGuiKey_C] = (uint32_t)KeyboardEvent::Key::C; - io.KeyMap[ImGuiKey_V] = (uint32_t)KeyboardEvent::Key::V; - io.KeyMap[ImGuiKey_X] = (uint32_t)KeyboardEvent::Key::X; - io.KeyMap[ImGuiKey_Y] = (uint32_t)KeyboardEvent::Key::Y; - io.KeyMap[ImGuiKey_Z] = (uint32_t)KeyboardEvent::Key::Z; - io.IniFilename = nullptr; - - ImGuiStyle& style = ImGui::GetStyle(); - style.Colors[ImGuiCol_WindowBg].w = 0.9f; - style.Colors[ImGuiCol_FrameBg].x *= 0.1f; - style.Colors[ImGuiCol_FrameBg].y *= 0.1f; - style.Colors[ImGuiCol_FrameBg].z *= 0.1f; - style.ScrollbarSize *= 0.7f; - - style.Colors[ImGuiCol_MenuBarBg] = style.Colors[ImGuiCol_WindowBg]; - style.ScaleAllSizes(scaleFactor); - - // Create the pipeline state cache - mpPipelineState = GraphicsState::create(); - - // Create the program - mpProgram = GraphicsProgram::createFromFile("Framework/Shaders/Gui.slang", "vs", "ps"); - mpProgramVars = GraphicsVars::create(mpProgram->getReflector()); - mpPipelineState->setProgram(mpProgram); - - // Add the default font - addFont("", "Framework/Fonts/trebucbd.ttf"); - setActiveFont(""); - - // Create the blend state - BlendState::Desc blendDesc; - blendDesc.setRtBlend(0, true).setRtParams(0, BlendState::BlendOp::Add, BlendState::BlendOp::Add, BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, BlendState::BlendFunc::Zero); - mpPipelineState->setBlendState(BlendState::create(blendDesc)); - - // Create the rasterizer state - RasterizerState::Desc rsDesc; - rsDesc.setFillMode(RasterizerState::FillMode::Solid).setCullMode(RasterizerState::CullMode::None).setScissorTest(true).setDepthClamp(false); - mpPipelineState->setRasterizerState(RasterizerState::create(rsDesc)); - - // Create the depth-stencil state - DepthStencilState::Desc dsDesc; - dsDesc.setDepthFunc(DepthStencilState::Func::Disabled); - mpPipelineState->setDepthStencilState(DepthStencilState::create(dsDesc)); - - // Create the VAO - VertexBufferLayout::SharedPtr pBufLayout = VertexBufferLayout::create(); - pBufLayout->addElement("POSITION", offsetof(ImDrawVert, pos), ResourceFormat::RG32Float, 1, 0); - pBufLayout->addElement("TEXCOORD", offsetof(ImDrawVert, uv), ResourceFormat::RG32Float, 1, 1); - pBufLayout->addElement("COLOR", offsetof(ImDrawVert, col), ResourceFormat::RGBA8Unorm, 1, 2); - mpLayout = VertexLayout::create(); - mpLayout->addBufferLayout(0, pBufLayout); - - mGuiImageLoc = mpProgram->getReflector()->getDefaultParameterBlock()->getResourceBinding("guiImage"); - } - - Gui::~Gui() - { - ImGui::DestroyContext(); - } - - Gui::UniquePtr Gui::create(uint32_t width, uint32_t height, float scaleFactor) - { - UniquePtr pGui = UniquePtr(new Gui); - pGui->init(scaleFactor); - pGui->onWindowResize(width, height); - return pGui; - } - - void Gui::createVao(uint32_t vertexCount, uint32_t indexCount) - { - static_assert(sizeof(ImDrawIdx) == sizeof(uint16_t), "ImDrawIdx expected size is a word"); - uint32_t requiredVbSize = vertexCount * sizeof(ImDrawVert); - uint32_t requiredIbSize = indexCount * sizeof(uint16_t); - bool createVB = true; - bool createIB = true; - - if (mpVao) - { - createVB = mpVao->getVertexBuffer(0)->getSize() <= requiredVbSize; - createIB = mpVao->getIndexBuffer()->getSize() <= requiredIbSize; - - if (!createIB && !createVB) - { - return; - } - } - - // Need to create a new VAO - std::vector pVB(1); - pVB[0] = createVB ? Buffer::create(requiredVbSize + sizeof(ImDrawVert) * 1000, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr) : mpVao->getVertexBuffer(0); - Buffer::SharedPtr pIB = createIB ? Buffer::create(requiredIbSize, Buffer::BindFlags::Index, Buffer::CpuAccess::Write, nullptr): mpVao->getIndexBuffer(); - mpVao = Vao::create(Vao::Topology::TriangleList, mpLayout, pVB, pIB, ResourceFormat::R16Uint); - } - - void Gui::onWindowResize(uint32_t width, uint32_t height) - { - ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize.x = (float)width; - io.DisplaySize.y = (float)height; -#ifdef FALCOR_VK - mpProgramVars["PerFrameCB"]["scale"] = 2.0f / vec2(io.DisplaySize.x, io.DisplaySize.y); - mpProgramVars["PerFrameCB"]["offset"] = vec2(-1.0f); -#else - mpProgramVars["PerFrameCB"]["scale"] = 2.0f / vec2(io.DisplaySize.x, -io.DisplaySize.y); - mpProgramVars["PerFrameCB"]["offset"] = vec2(-1.0f, 1.0f); -#endif - } - - void Gui::setIoMouseEvents() - { - ImGuiIO& io = ImGui::GetIO(); - memcpy(io.MouseDown, mMouseEvents.buttonPressed, sizeof(mMouseEvents.buttonPressed)); - } - - void Gui::resetMouseEvents() - { - for (uint32_t i = 0; i < arraysize(mMouseEvents.buttonPressed); i++) - { - if (mMouseEvents.buttonReleased[i]) - { - mMouseEvents.buttonPressed[i] = mMouseEvents.buttonReleased[i] = false; - } - } - } - - void Gui::beginFrame() - { - ImGui::NewFrame(); - } - - void Gui::render(RenderContext* pContext, float elapsedTime) - { - while (mGroupStackSize) - { - endGroup(); - } - - pContext->setGraphicsVars(mpProgramVars); - // Set the mouse state - setIoMouseEvents(); - - ImGui::Render(); - ImDrawData* pDrawData = ImGui::GetDrawData(); - - resetMouseEvents(); - // Update the VAO - - createVao(pDrawData->TotalVtxCount, pDrawData->TotalIdxCount); - mpPipelineState->setVao(mpVao); - - // Upload the data - ImDrawVert* pVerts = (ImDrawVert*)mpVao->getVertexBuffer(0)->map(Buffer::MapType::WriteDiscard); - uint16_t* pIndices = (uint16_t*)mpVao->getIndexBuffer()->map(Buffer::MapType::WriteDiscard); - - for (int n = 0; n < pDrawData->CmdListsCount; n++) - { - const ImDrawList* pCmdList = pDrawData->CmdLists[n]; - memcpy(pVerts, pCmdList->VtxBuffer.Data, pCmdList->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(pIndices, pCmdList->IdxBuffer.Data, pCmdList->IdxBuffer.Size * sizeof(ImDrawIdx)); - pVerts += pCmdList->VtxBuffer.Size; - pIndices += pCmdList->IdxBuffer.Size; - } - mpVao->getVertexBuffer(0)->unmap(); - mpVao->getIndexBuffer()->unmap(); - mpPipelineState->setFbo(pContext->getGraphicsState()->getFbo()); - pContext->pushGraphicsState(mpPipelineState); - - // Setup viewport - GraphicsState::Viewport vp; - vp.originX = 0; - vp.originY = 0; - vp.width = ImGui::GetIO().DisplaySize.x; - vp.height = ImGui::GetIO().DisplaySize.y; - vp.minDepth = 0; - vp.maxDepth = 1; - mpPipelineState->setViewport(0, vp); - - // Render command lists - uint32_t vtxOffset = 0; - uint32_t idxOffset = 0; - - for (int n = 0; n < pDrawData->CmdListsCount; n++) - { - const ImDrawList* pCmdList = pDrawData->CmdLists[n]; - for (int32_t cmd = 0; cmd < pCmdList->CmdBuffer.Size; cmd++) - { - const ImDrawCmd* pCmd = &pCmdList->CmdBuffer[cmd]; - GraphicsState::Scissor scissor((int32_t)pCmd->ClipRect.x, (int32_t)pCmd->ClipRect.y, (int32_t)pCmd->ClipRect.z, (int32_t)pCmd->ClipRect.w); - if (pCmd->TextureId) - { - mpProgramVars->getDefaultBlock()->setSrv(mGuiImageLoc, 0, (mpImages[reinterpret_cast(pCmd->TextureId) - 1])->getSRV()); - mpProgramVars["PerFrameCB"]["useGuiImage"] = true; - } - else - { - mpProgramVars["PerFrameCB"]["useGuiImage"] = false; - } - mpPipelineState->setScissors(0, scissor); - pContext->drawIndexed(pCmd->ElemCount, idxOffset, vtxOffset); - idxOffset += pCmd->ElemCount; - } - vtxOffset += pCmdList->VtxBuffer.Size; - } - - // Prepare for the next frame - ImGuiIO& io = ImGui::GetIO(); - io.DeltaTime = elapsedTime; - mGroupStackSize = 0; - pContext->popGraphicsState(); - - mpImages.clear(); - } - - glm::vec4 Gui::pickUniqueColor(const std::string& key) - { - union hashedValue - { - size_t st; - int32_t i32[2]; - }; - hashedValue color; - color.st = std::hash()(key); - - return glm::vec4(color.i32[0] % 1000 / 2000.0f, color.i32[1] % 1000 / 2000.0f, (color.i32[0] * color.i32[1]) % 1000 / 2000.0f, 1.0f); - } - - void Gui::addDummyItem(const char label[], const glm::vec2& size, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - ImGui::PushID(label); - ImGui::Dummy({ size.x, size.y }); - ImGui::PopID(); - } - - void Gui::addRect(const glm::vec2& size, const glm::vec4& color, bool filled, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - - const ImVec2& cursorPos = ImGui::GetCursorScreenPos(); - ImVec2 bottomLeft{ cursorPos.x + size.x, cursorPos.y + size.y }; - ImVec4 rectColor{color.x, color.y, color.z, color.w}; - - if (filled) - { - ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); - } - else - { - ImGui::GetWindowDrawList()->AddRect(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); - } - } - - void Gui::beginColumns(uint32_t numColumns) - { - ImGui::Columns(numColumns); - } - - void Gui::nextColumn() - { - ImGui::NextColumn(); - } - - bool Gui::addCheckBox(const char label[], bool& var, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - return ImGui::Checkbox(label, &var); - } - - bool Gui::addCheckBox(const char label[], int& var, bool sameLine) - { - bool value = (var != 0); - bool modified = addCheckBox(label, value, sameLine); - var = (value ? 1 : 0); - return modified; - } - - bool Gui::addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine) - { - bool modified = false; - std::string labelString(std::string("##") + label + '0'); - - for (uint32_t i = 0; i < numCheckboxes - 1; ++i) - { - labelString[labelString.size() - 1] = '0' + static_cast(i); - modified |= addCheckBox(labelString.c_str(), pData[i], (!i) ? sameLine : true); - } - - addCheckBox(label, pData[numCheckboxes - 1], true ); - - return modified; - } - - bool Gui::addBool2Var(const char label[], glm::bvec2& var, bool sameLine) - { - return addCheckboxes(label, glm::value_ptr(var), 2, sameLine); - } - - bool Gui::addBool3Var(const char label[], glm::bvec3& var, bool sameLine) - { - return addCheckboxes(label, glm::value_ptr(var), 3, sameLine); - } - - bool Gui::addBool4Var(const char label[], glm::bvec4& var, bool sameLine) - { - return addCheckboxes(label, glm::value_ptr(var), 4, sameLine); - } - - void Gui::addText(const char text[], bool sameLine) - { - if (sameLine) ImGui::SameLine(); - ImGui::Text(text, ""); - } - - bool Gui::addButton(const char label[], bool sameLine) - { - if (sameLine) ImGui::SameLine(); - return ImGui::Button(label); - } - - bool Gui::addRadioButtons(const RadioButtonGroup& buttons, uint32_t& activeID) - { - int32_t oldValue = activeID; - - for (const auto& button : buttons) - { - if (button.sameLine) ImGui::SameLine(); - ImGui::RadioButton(button.label.c_str(), (int*)&activeID, button.buttonID); - } - - return oldValue != activeID; - } - - bool Gui::beginGroup(const char name[], bool beginExpanded) - { - std::string nameString(name); - ImGuiTreeNodeFlags flags = beginExpanded ? ImGuiTreeNodeFlags_DefaultOpen : 0; - if (mOpenWindows[nameString]) ImGui::SetNextTreeNodeOpen(true); - bool visible = mGroupStackSize ? ImGui::TreeNodeEx(name, flags) : ImGui::CollapsingHeader(name, flags); - if (visible) - { - mGroupStackSize++; - } - - std::string popupName = std::string("HeaderOptions##") + nameString; - if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(1)) { ImGui::OpenPopup(popupName.c_str()); } - - if (ImGui::BeginPopup(popupName.c_str())) - { - if (ImGui::Button("Open in Window")) - { - mOpenWindows[nameString] = true; - ImGui::CloseCurrentPopup(); - } - if(ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup();} - ImGui::EndPopup(); - } - - if (visible && mOpenWindows.find(nameString) != mOpenWindows.end()) - { - const ImGuiIO& io = ImGui::GetIO(); - uint32_t w = (uint32_t)(io.DisplaySize.x * 0.25f); - uint32_t h = (uint32_t)(io.DisplaySize.y * 0.4f); - uint32_t y = 20; - uint32_t x = static_cast(io.DisplaySize.x) - w - 20; - pushWindow(name, w, h, x, y, true, true, true, true); - mGroupHasWindow.push_back(mOpenWindows[nameString]); - } - - return visible; - } - - void Gui::endGroup() - { - assert(mGroupStackSize >= 1); - mGroupStackSize--; - - if (mGroupHasWindow.back()) - { - popWindow(); - } - if (mGroupStackSize) - { - ImGui::TreePop(); - } - mGroupHasWindow.pop_back(); - } - - bool Gui::addFloatVar(const char label[], float& var, float minVal, float maxVal, float step, bool sameLine, const char* displayFormat) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::DragFloat(label, &var, step, minVal, maxVal, displayFormat); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addFloat2Var(const char label[], glm::vec2& var, float minVal, float maxVal, float step, bool sameLine, const char* displayFormat) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::DragFloat2(label, glm::value_ptr(var), step, minVal, maxVal, displayFormat); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addFloat3Var(const char label[], glm::vec3& var, float minVal, float maxVal, float step, bool sameLine, const char* displayFormat) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::DragFloat3(label, glm::value_ptr(var), step, minVal, maxVal, displayFormat); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addFloat4Var(const char label[], glm::vec4& var, float minVal, float maxVal, float step, bool sameLine, const char* displayFormat) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::DragFloat4(label, glm::value_ptr(var), step, minVal, maxVal, displayFormat); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addIntVar(const char label[], int32_t& var, int minVal, int maxVal, int step, bool sameLine) - { - ImGui::PushItemWidth(200); - if (sameLine) ImGui::SameLine(); - bool b = ImGui::InputInt(label, &var, step); - var = clamp(var, minVal, maxVal); - ImGui::PopItemWidth(); - return b; - } - - bool Gui::addInt2Var(const char label[], glm::ivec2& var, int32_t minVal, int32_t maxVal, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::InputInt2(label, static_cast(glm::value_ptr(var)), 0); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addInt3Var(const char label[], glm::ivec3& var, int32_t minVal, int32_t maxVal, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::InputInt3(label, static_cast(glm::value_ptr(var)), 0); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addInt4Var(const char label[], glm::ivec4& var, int32_t minVal, int32_t maxVal, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::InputInt4(label, static_cast(glm::value_ptr(var)), 0); - var = clamp(var, minVal, maxVal); - return b; - } - - bool Gui::addIntSlider(const char label[], int32_t& var, int minVal, int maxVal, bool sameLine) - { - ImGui::PushItemWidth(200); - if (sameLine) ImGui::SameLine(); - bool b = ImGui::SliderInt(label, &var, minVal, maxVal); - var = clamp(var, minVal, maxVal); - ImGui::PopItemWidth(); - return b; - } - - bool Gui::addFloatSlider(const char label[], float& var, float minVal, float maxVal, bool sameLine, const char* displayFormat) - { - if (sameLine) ImGui::SameLine(); - bool b = ImGui::SliderFloat(label, &var, minVal, maxVal); - var = clamp(var, minVal, maxVal); - return b; - } - -#define add_int_slider(FuncName, SliderFunc, TypeName, Type) \ - bool Gui::FuncName(const char label[], TypeName& var, Type minVal, Type maxVal, bool sameLine) \ - {\ - if (sameLine) ImGui::SameLine();\ - bool b = ImGui::SliderFunc(label, glm::value_ptr(var), minVal, maxVal);\ - var = clamp(var, minVal, maxVal);\ - return b;\ - } - - add_int_slider(addInt2Slider, SliderInt2, glm::ivec2, int32_t); - add_int_slider(addInt3Slider, SliderInt3, glm::ivec3, int32_t); - add_int_slider(addInt4Slider, SliderInt4, glm::ivec4, int32_t); - -#define add_float_slider(FuncName, SliderFunc, TypeName, Type) \ - bool Gui::FuncName(const char label[], TypeName& var, Type minVal, Type maxVal, bool sameLine, const char* displayFormat) \ - {\ - if (sameLine) ImGui::SameLine();\ - bool b = ImGui::SliderFunc(label, glm::value_ptr(var), minVal, maxVal);\ - var = clamp(var, minVal, maxVal);\ - return b;\ - } - - add_float_slider(addFloat2Slider, SliderFloat2, glm::vec2, float); - add_float_slider(addFloat3Slider, SliderFloat3, glm::vec3, float); - add_float_slider(addFloat4Slider, SliderFloat4, glm::vec4, float); - - template <> - bool Gui::addFloatVecVar(const char label[], glm::vec2& var, float minVal, float maxVal, float step, bool sameLine) - { - return addFloat2Var(label, var, minVal, maxVal, step, sameLine); - } - - template <> - bool Gui::addFloatVecVar(const char label[], glm::vec3& var, float minVal, float maxVal, float step, bool sameLine) - { - return addFloat3Var(label, var, minVal, maxVal, step, sameLine); - } - - template <> - bool Gui::addFloatVecVar(const char label[], glm::vec4& var, float minVal, float maxVal, float step, bool sameLine) - { - return addFloat4Var(label, var, minVal, maxVal, step, sameLine); - } - -#define add_matrix_var(TypeName) template bool Gui::addMatrixVar(const char label[], TypeName& var, float minVal , float maxVal, bool sameLine) - - add_matrix_var(glm::mat2x2); - add_matrix_var(glm::mat2x3); - add_matrix_var(glm::mat2x4); - add_matrix_var(glm::mat3x2); - add_matrix_var(glm::mat3x3); - add_matrix_var(glm::mat3x4); - add_matrix_var(glm::mat4x2); - add_matrix_var(glm::mat4x3); - add_matrix_var(glm::mat4x4); - -#undef add_matrix_var - - - template - bool Gui::addMatrixVar(const char label[], MatrixType& var, float minVal, float maxVal, bool sameLine) - { - std::string labelString(label); - std::string hiddenLabelString("##"); - hiddenLabelString += labelString + "[0]"; - - ImVec2 topLeft = ImGui::GetCursorScreenPos(); - ImVec2 bottomRight; - - bool b = false; - - for (uint32_t i = 0; i < static_cast(var.length()); ++i) - { - std::string& stringToDisplay = hiddenLabelString; - hiddenLabelString[hiddenLabelString.size() - 2] = '0' + static_cast(i); - if (i == var.length() - 1) - { - stringToDisplay = labelString; - } - - b |= addFloatVecVar(stringToDisplay.c_str(), var[i], minVal, maxVal, 0.001f, sameLine); - - if (i == 0) - { - ImGui::SameLine(); - bottomRight = ImGui::GetCursorScreenPos(); - float oldSpacing = ImGui::GetStyle().ItemSpacing.y; - ImGui::GetStyle().ItemSpacing.y = 0.0f; - ImGui::Dummy({}); - ImGui::Dummy({}); - ImGui::GetStyle().ItemSpacing.y = oldSpacing; - ImVec2 correctedCursorPos = ImGui::GetCursorScreenPos(); - correctedCursorPos.y += oldSpacing; - ImGui::SetCursorScreenPos(correctedCursorPos); - bottomRight.y = ImGui::GetCursorScreenPos().y; - } - else if(i == 1) - { - bottomRight.y = topLeft.y + (bottomRight.y - topLeft.y) * (var.length()); - bottomRight.x -= ImGui::GetStyle().ItemInnerSpacing.x * 3 - 1; - bottomRight.y -= ImGui::GetStyle().ItemInnerSpacing.y - 1; - topLeft.x -= 1; topLeft.y -= 1; - auto colorVec4 =ImGui::GetStyleColorVec4(ImGuiCol_ScrollbarGrab); colorVec4.w *= 0.25f; - ImU32 color = ImGui::ColorConvertFloat4ToU32(colorVec4); - ImGui::GetWindowDrawList()->AddRect(topLeft, bottomRight, color); - } - } - return b; - } - - bool Gui::beginMainMenuBar() - { - bool isOpen = ImGui::BeginMainMenuBar(); - ImGui::SetWindowFocus(); - return isOpen; - } - - bool Gui::beginDropDownMenu(const char label[]) - { - return ImGui::BeginMenu(label); - } - - void Gui::endDropDownMenu() - { - ImGui::EndMenu(); - } - - bool Gui::addMenuItem(const char label[]) - { - return ImGui::MenuItem(label); - } - - bool Gui::addMenuItem(const char label[], bool& var) - { - return ImGui::MenuItem(label, nullptr, &var); - } - - void Gui::endMainMenuBar() - { - ImGui::EndMainMenuBar(); - } - - bool Gui::addRgbColor(const char label[], glm::vec3& var, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - return ImGui::ColorEdit3(label, glm::value_ptr(var)); - } - - bool Gui::addRgbaColor(const char label[], glm::vec4& var, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - return ImGui::ColorEdit4(label, glm::value_ptr(var)); - } - - bool Gui::dragDropSource(const char label[], const char dataLabel[], const std::string& payloadString) - { - if (ImGui::IsItemHovered() && (ImGui::IsMouseClicked(0) || ImGui::IsMouseClicked(1))) ImGui::SetWindowFocus(); - if (!(ImGui::IsWindowFocused())) return false; - bool b = ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID); - if (b) - { - ImGui::SetDragDropPayload(dataLabel, payloadString.data(), payloadString.size() * sizeof(payloadString[0]), ImGuiCond_Once); - ImGui::EndDragDropSource(); - } - return b; - } - - bool Gui::dragDropDest(const char dataLabel[], std::string& payloadString) - { - bool b = false; - if (ImGui::BeginDragDropTarget()) - { - auto dragDropPayload = ImGui::AcceptDragDropPayload(dataLabel); - b = dragDropPayload && dragDropPayload->IsDataType(dataLabel) && (dragDropPayload->Data != nullptr); - if (b) - { - payloadString.resize(dragDropPayload->DataSize); - std::memcpy(&payloadString.front(), dragDropPayload->Data, dragDropPayload->DataSize); - } - - ImGui::EndDragDropTarget(); - } - - return b; - } - - bool Gui::onKeyboardEvent(const KeyboardEvent& event) - { - ImGuiIO& io = ImGui::GetIO(); - - if (event.type == KeyboardEvent::Type::Input) - { - std::string u8str = utf32ToUtf8(event.codepoint); - io.AddInputCharactersUTF8(u8str.c_str()); - - // Gui consumes keyboard input - return true; - } - else - { - uint32_t key = (uint32_t)(event.key == KeyboardEvent::Key::KeypadEnter ? KeyboardEvent::Key::Enter : event.key); - - switch (event.type) - { - case KeyboardEvent::Type::KeyPressed: - io.KeysDown[key] = true; - break; - case KeyboardEvent::Type::KeyReleased: - io.KeysDown[key] = false; - break; - default: - should_not_get_here(); - } - - io.KeyCtrl = event.mods.isCtrlDown; - io.KeyAlt = event.mods.isAltDown; - io.KeyShift = event.mods.isShiftDown; - io.KeySuper = false; - return io.WantCaptureKeyboard; - } - } - - void Gui::addImage(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio, bool sameLine) - { - if (size == vec2(0)) size = getCurrentWindowSize(); - - ImGui::PushID(label); - if (sameLine) ImGui::SameLine(); - mpImages.push_back(pTex); - float aspectRatio = maintainRatio ? (static_cast(pTex->getHeight()) / static_cast(pTex->getWidth())) : 1.0f; - ImGui::Image(reinterpret_cast(mpImages.size()), { size.x, maintainRatio ? size.x * aspectRatio : size.y }); - ImGui::PopID(); - } - - bool Gui::addImageButton(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio, bool sameLine) - { - mpImages.push_back(pTex); - float aspectRatio = maintainRatio ? (static_cast(pTex->getHeight()) / static_cast(pTex->getWidth())) : 1.0f; - return ImGui::ImageButton(reinterpret_cast(mpImages.size()), { size.x, maintainRatio ? size.x * aspectRatio : size.y }); - } - - bool Gui::onMouseEvent(const MouseEvent& event) - { - ImGuiIO& io = ImGui::GetIO(); - switch (event.type) - { - case MouseEvent::Type::LeftButtonDown: - mMouseEvents.buttonPressed[0] = true; - break; - case MouseEvent::Type::LeftButtonUp: - mMouseEvents.buttonReleased[0] = true; - break; - case MouseEvent::Type::RightButtonDown: - mMouseEvents.buttonPressed[1] = true; - break; - case MouseEvent::Type::RightButtonUp: - mMouseEvents.buttonReleased[1] = true; - break; - case MouseEvent::Type::MiddleButtonDown: - mMouseEvents.buttonPressed[2] = true; - break; - case MouseEvent::Type::MiddleButtonUp: - mMouseEvents.buttonReleased[2] = true; - break; - case MouseEvent::Type::Move: - io.MousePos.x = event.pos.x * io.DisplaySize.x; - io.MousePos.y = event.pos.y * io.DisplaySize.y; - break; - case MouseEvent::Type::Wheel: - io.MouseWheel += event.wheelDelta.y; - break; - } - - return io.WantCaptureMouse; - } - - void Gui::pushWindow(const char label[], uint32_t width, uint32_t height, uint32_t x, uint32_t y, bool showTitleBar, bool allowMove, bool focus, bool allowClose) - { - if (mOpenWindows.find(label) == mOpenWindows.end()) mOpenWindows[label] = true; - if (allowClose) - { - if (!showTitleBar) - { - std::string warning("showTitleBar is set to false on the window "); - logWarning(warning.append(label).append(". The window will not be able to display a close button.")); - } - if (!mOpenWindows[label]) return; - } - - ImVec2 pos{ float(x), float(y) }; - ImVec2 size{ float(width), float(height) }; - ImGui::SetNextWindowSize(size, ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(pos, ImGuiCond_FirstUseEver); - int flags = 0; - if (!showTitleBar) flags |= ImGuiWindowFlags_NoTitleBar; - if (!allowMove) flags |= ImGuiWindowFlags_NoMove; - if (!focus) flags |= ImGuiWindowFlags_NoFocusOnAppearing; - - ImGui::Begin(label, allowClose ? &mOpenWindows[label] : nullptr, flags); - - if (allowClose && !mOpenWindows[label]) ImGui::End(); - else ImGui::PushFont(mpActiveFont); - } - - void Gui::popWindow() - { - ImGui::PopFont(); - ImGui::End(); - } - - void Gui::setCurrentWindowPos(uint32_t x, uint32_t y) - { - ImGui::SetWindowPos({ static_cast(x), static_cast(y) }); - } - - glm::vec2 Gui::getCurrentWindowPos() - { - ImVec2 windowPos = ImGui::GetWindowPos(); - return { windowPos.x, windowPos.y }; - } - - void Gui::setCurrentWindowSize(uint32_t width, uint32_t height) - { - ImGui::SetWindowSize({ static_cast(width), static_cast(height) }); - } - - glm::vec2 Gui::getCurrentWindowSize() - { - ImVec2 windowSize = ImGui::GetWindowSize(); - return {windowSize.x, windowSize.y}; - } - - void Gui::addSeparator() - { - ImGui::Separator(); - } - - bool Gui::addDropdown(const char label[], const DropdownList& values, uint32_t& var, bool sameLine) - { - if (sameLine) ImGui::SameLine(); - // Check if we need to update the currentItem - const auto& iter = mDropDownValues.find(label); - int curItem; - if ((iter == mDropDownValues.end()) || (iter->second.lastVal != var)) - { - // Search the current val - for (uint32_t i = 0 ; i < values.size() ; i++) - { - if (values[i].value == var) - { - curItem = i; - mDropDownValues[label].currentItem = i; - break; - } - } - } - else - { - curItem = mDropDownValues[label].currentItem; - } - - std::string comboStr; - for (const auto& v : values) - { - comboStr += v.label + '\0'; - } - comboStr += '\0'; - uint32_t prevItem = curItem; - //This returns true if the combo is interacted with at all - bool b = ImGui::Combo(label, &curItem, comboStr.c_str()); - mDropDownValues[label].currentItem = curItem; - mDropDownValues[label].lastVal = values[curItem].value; - var = values[curItem].value; - //Only return true if value is changed - return b && prevItem != curItem; - } - - void Gui::setGlobalGuiScaling(float scale) - { - ImGuiIO& io = ImGui::GetIO(); - io.FontGlobalScale = scale; - ImGui::GetStyle().ScaleAllSizes(scale); - } - - bool Gui::addTextBox(const char label[], char buf[], size_t bufSize, uint32_t lineCount) - { - const ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue; - - if (lineCount > 1) - { - return ImGui::InputTextMultiline(label, buf, bufSize, ImVec2(-1.0f, ImGui::GetTextLineHeight() * lineCount), flags); - } - else - { - return ImGui::InputText(label, buf, bufSize, flags); - } - } - - bool Gui::addTextBox(const char label[], std::string& text, uint32_t lineCount /*= 1*/) - { - const ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue; - - static const int maxSize = 2048; - char buf[maxSize]; - copyStringToBuffer(buf, maxSize, text); - - bool result = false; - if (lineCount > 1) - { - result = ImGui::InputTextMultiline(label, buf, maxSize, ImVec2(-1.0f, ImGui::GetTextLineHeight() * lineCount), flags); - } - else - { - result = ImGui::InputText(label, buf, maxSize, flags); - } - - text = std::string(buf); - return result; - } - - bool Gui::addMultiTextBox(const char label[], const std::vector& textLabels, std::vector& textEntries) - { - static uint32_t sIdOffset = 0; - bool result = false; - - for (uint32_t i = 0; i < textEntries.size(); ++i) - { - result |= addTextBox(std::string(textLabels[i] + "##" + std::to_string(sIdOffset)).c_str(), textEntries[i]); - } - - return addButton(label) | result; - } - - void Gui::addTooltip(const char tip[], bool sameLine) - { - if (sameLine) ImGui::SameLine(); - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(450.0f); - ImGui::TextUnformatted(tip); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - } - - void Gui::addGraph(const char label[], GraphCallback func, void* pUserData, uint32_t sampleCount, int32_t sampleOffset, float yMin, float yMax, uint32_t width, uint32_t height) - { - ImVec2 imSize{ (float)width, (float)height }; - ImGui::PlotLines(label, func, pUserData, (int32_t)sampleCount, sampleOffset, nullptr, yMin, yMax, imSize); - } - - bool Gui::addDirectionWidget(const char label[], glm::vec3& direction) - { - glm::vec3 dir = direction; - bool b = addFloat3Var(label, dir, -1, 1); - direction = glm::normalize(dir); - return b; - } - - void Gui::compileFonts() - { - uint8_t* pFontData; - int32_t width, height; - - // Initialize font data - ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pFontData, &width, &height); - Texture::SharedPtr pTexture = Texture::create2D(width, height, ResourceFormat::R8Unorm, 1, 1, pFontData); - mpProgramVars->setTexture("gFont", pTexture); - } - - void Gui::addFont(const std::string& name, const std::string& filename) - { - std::string fullpath; - if (findFileInDataDirectories(filename, fullpath) == false) - { - logWarning("Can't find font file `" + filename + "`"); - return; - } - - float size = 14.0f * mScaleFactor; - ImFont* pFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(fullpath.c_str(), size); - mFontMap[name] = pFont; - compileFonts(); - } - - void Gui::setActiveFont(const std::string& font) - { - const auto& it = mFontMap.find(font); - if (it == mFontMap.end()) - { - logWarning("Can't find a font named `" + font + "`"); - mpActiveFont = nullptr; - } - mpActiveFont = it->second; - } - - bool Gui::isWindowOpen(const char label[]) - { - const auto it = mOpenWindows.find(std::string(label)); - return (it != mOpenWindows.end()) && it->second; - } - - void Gui::setWindowOpen(const char label[], bool isOpen) - { - std::string labelString(label); - auto it = mOpenWindows.find(labelString); - if (it == mOpenWindows.end()) logWarning(std::string("Gui Window ") + labelString + " does not exist."); - it->second = isOpen; - } -} diff --git a/Framework/Source/Utils/Gui.h b/Framework/Source/Utils/Gui.h deleted file mode 100644 index 59f038695..000000000 --- a/Framework/Source/Utils/Gui.h +++ /dev/null @@ -1,462 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "glm/vec3.hpp" -#include "UserInput.h" -#include "Graphics/Program//ProgramVars.h" -#include "Graphics/Program//Program.h" -#include "Graphics/GraphicsState.h" - -struct ImFont; - -namespace Falcor -{ - class RenderContext; - struct KeyboardEvent; - struct MouseEvent; - - /** A class wrapping the external GUI library - */ - class Gui - { - friend class Sample; - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - /** These structs used to initialize dropdowns - */ - struct DropdownValue - { - uint32_t value; ///< User defined index. Should be unique between different options. - std::string label; ///< Label of the dropdown option. - }; - - using DropdownList = std::vector ; - - struct RadioButton - { - uint32_t buttonID; ///< User defined index. Should be unique between different options in the same group. - std::string label; ///< Label of the radio button. - bool sameLine; ///< Whether the button should appear on the same line as the previous widget/button. - }; - - using RadioButtonGroup = std::vector; - - /** Create a new GUI object. Each object is essentially a container for a GUI window - */ - static UniquePtr create(uint32_t width, uint32_t height, float scaleFactor = 1.0f); - - ~Gui(); - - static glm::vec4 pickUniqueColor(const std::string& key); - - /** Render the GUI - */ - void render(RenderContext* pContext, float elapsedTime); - - /** Handle window resize events - */ - void onWindowResize(uint32_t width, uint32_t height); - - /** Handle mouse events - */ - bool onMouseEvent(const MouseEvent& event); - - /** Handle keyboard events - */ - bool onKeyboardEvent(const KeyboardEvent& event); - - /** Display image within imgui - \param[in] label. Name for id for item. - \param[in] pTex. Pointer to texture resource to draw in imgui - \param[in] size. Size in pixels of the image to draw. 0 means fit to window - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - */ - void addImage(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size = vec2(0), bool maintainRatio = true, bool sameLine = false); - - bool addImageButton(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio = true, bool sameLine = false); - - /** Display rectangle with specified color - \param[in] size size in pixels of rectangle - \param[in] color Optional. color as an rgba vec4 - \param[in] filled Optional. If set to true, rectangle will be filled - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - */ - void addRect(const glm::vec2& size, const glm::vec4& color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), bool filled = false, bool sameLine = false); - - /** Dummy object especially useful for spacing - \param[in] label. Name for id of item - \param[in] size. size in pixels of the item. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - */ - void addDummyItem(const char label[], const glm::vec2& size, bool sameLine = false); - - /** Static text - \param[in] text The string to display - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - */ - void addText(const char text[], bool sameLine = false); - - /** Button. Will return true if the button was pressed - \param[in] label Text to display on the button - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - */ - bool addButton(const char label[], bool sameLine = false); - - /** Adds a group of radio buttons. - \param[in] buttons List of buttons to show. - \param[out] activeID If a button was clicked, activeID will be set to the ID of the clicked button. - \return Whether activeID changed. - */ - bool addRadioButtons(const RadioButtonGroup& buttons, uint32_t& activeID); - - /** Begin a collapsible group block - \param[in] label Display name of the group - \param[in] beginExpanded Whether group should be expanded initially - \return Returns true if the group is expanded, otherwise false. Use it to avoid making unnecessary calls - */ - bool beginGroup(const char label[], bool beginExpanded = false); - bool beginGroup(const std::string& label, bool beginExpanded = false) { return beginGroup(label.c_str(), beginExpanded); } - - /** End a collapsible group block - */ - void endGroup(); - - /** Begins main menu bar for top of the window - */ - bool beginMainMenuBar(); - - /** Begins a collapsable menu in the main menu bar of menu items - \param[in] label name of drop down menu to be displayed. - */ - bool beginDropDownMenu(const char label[]); - - /** End the drop down menu list of items. - */ - void endDropDownMenu(); - - /** Item to be displayed in dropdown menu for the main menu bar - \param[in] label name of item to list in the menu. - \param[in] var if the label is selected or not. - \return true if the option was selected in the dropdown - */ - bool addMenuItem(const char label[], bool& var); - - /** Item to be displayed in dropdown menu for the main menu bar - \param[in] label name of item to list in the menu. - \return true if the option was selected in the dropdown - */ - bool addMenuItem(const char label[]); - - /** Ends the main menu bar for top of the window - */ - void endMainMenuBar(); - - /** Adds a floating-point UI element. - \param[in] label The name of the widget. - \param[in] var A reference to a float that will be updated directly when the widget state changes. - \param[in] minVal Optional. The minimum allowed value for the float. - \param[in] maxVal Optional. The maximum allowed value for the float. - \param[in] step Optional. The step rate for the float. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget. - \param[in] displayFormat Optional. Formatting string. - \return true if the value changed, otherwise false - */ - bool addFloatVar(const char label[], float& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, float step = 0.001f, bool sameLine = false, const char* displayFormat = "%.3f"); - bool addFloat2Var(const char label[], glm::vec2& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, float step = 0.001f, bool sameLine = false, const char* displayFormat = "%.3f"); - bool addFloat3Var(const char label[], glm::vec3& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, float step = 0.001f, bool sameLine = false, const char* displayFormat = "%.3f"); - bool addFloat4Var(const char label[], glm::vec4& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, float step = 0.001f, bool sameLine = false, const char* displayFormat = "%.3f"); - - bool addFloatSlider(const char label[], float& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false, const char* displayFormat = "%.3f"); - bool addFloat2Slider(const char label[], glm::vec2& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false, const char* displayFormat = "%.3f"); - bool addFloat3Slider(const char label[], glm::vec3& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false, const char* displayFormat = "%.3f"); - bool addFloat4Slider(const char label[], glm::vec4& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false, const char* displayFormat = "%.3f"); - - /** Adds a checkbox. - \param[in] label The name of the checkbox. - \param[in] var A reference to a boolean that will be updated directly when the checkbox state changes. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addCheckBox(const char label[], bool& pVar, bool sameLine = false); - - /** Adds a checkbox. - \param[in] label The name of the checkbox. - \param[in] var A reference to an integer that will be updated directly when the checkbox state changes (0 = unchecked, 1 = checked). - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addCheckBox(const char label[], int& pVar, bool sameLine = false); - - /** Adds a UI widget for multiple checkboxes. - \param[in] label The name of the widget. - \param[in] var A reference to the bools that will be updated directly when the checkbox state changes (0 = unchecked, 1 = checked). - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addBool2Var(const char lable[], glm::bvec2& var, bool sameLine = false); - bool addBool3Var(const char lable[], glm::bvec3& var, bool sameLine = false); - bool addBool4Var(const char lable[], glm::bvec4& var, bool sameLine = false); - - /** Adds an RGB color UI widget. - \param[in] label The name of the widget. - \param[in] var A reference to a vector that will be updated directly when the widget state changes. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addRgbColor(const char label[], glm::vec3& var, bool sameLine = false); - - /** Adds an RGBA color UI widget. - \param[in] label The name of the widget. - \param[in] var A reference to a vector that will be updated directly when the widget state changes. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addRgbaColor(const char label[], glm::vec4& var, bool sameLine = false); - - /** The source for drag and drop. Call this to allow users to drag out of last gui item. - \param[in] label The name of the drag and drop widget - \param[in] dataLabel Destination that has same dataLabel can accept the payload - \param[in] payloadString Data in payload to be sent and accepted by destination. - \return true if user is clicking and dragging - */ - bool dragDropSource(const char label[], const char dataLabel[], const std::string& payloadString); - - /** Destination area for dropping data in drag and drop of last gui item. - \param[in] dataLabel Named label needs to be the same as source datalabel to accept payload. - \param[in] payloadString Data sent from the drag and drop source - \return true if payload is dropped. - */ - bool dragDropDest(const char dataLabel[], std::string& payloadString); - - /** Adds a UI widget for integers. - \param[in] label The name of the widget. - \param[in] var A reference to an integer that will be updated directly when the widget state changes. - \param[in] minVal Optional. The minimum allowed value for the variable. - \param[in] maxVal Optional. The maximum allowed value for the variable. - \param[in] step Optional. The step rate. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addIntVar(const char label[], int32_t& var, int minVal = -INT32_MAX, int maxVal = INT32_MAX, int step = 1, bool sameLine = false); - bool addInt2Var(const char label[], glm::ivec2& var, int32_t minVal = -INT32_MAX, int32_t maxVal = INT32_MAX, bool sameLine = false); - bool addInt3Var(const char label[], glm::ivec3& var, int32_t minVal = -INT32_MAX, int32_t maxVal = INT32_MAX, bool sameLine = false); - bool addInt4Var(const char label[], glm::ivec4& var, int32_t minVal = -INT32_MAX, int32_t maxVal = INT32_MAX, bool sameLine = false); - - bool addIntSlider(const char label[], int32_t& var, int minVal = -INT32_MAX, int maxVal = INT32_MAX, bool sameLine = false); - bool addInt2Slider(const char label[], glm::ivec2& var, int32_t minVal = -INT32_MAX, int32_t maxVal = INT32_MAX, bool sameLine = false); - bool addInt3Slider(const char label[], glm::ivec3& var, int32_t minVal = -INT32_MAX, int32_t maxVal = INT32_MAX, bool sameLine = false); - bool addInt4Slider(const char label[], glm::ivec4& var, int32_t minVal = -INT32_MAX, int32_t maxVal = INT32_MAX, bool sameLine = false); - - template - bool addFloatVecVar(const char label[], VectorType& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, float step = 0.001f, bool sameLine = false); - - /** Adds an matrix UI widget. - \param[in] label The name of the widget. - \param[in] var A reference to the matrix struct that will be updated directly when the widget state changes. - \param[in] minVal Optional. The minimum allowed value for the variable. - \param[in] maxVal Optional. The maximum allowed value for the variable. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - template - bool addMatrixVar(const char label[], MatrixType& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false); - - /** Begin a column within the current window - \param[in] numColumns requires number of columns within the window. - */ - void beginColumns(uint32_t numColumns); - - /** Proceed to the next column within the window. - */ - void nextColumn(); - - /** Add a separator - */ - void addSeparator(); - - /** Adds a dropdown menu. This will update a user variable directly, so the user has to keep track of that for changes. - If you want notifications whenever the select option changed, use Gui#addDropdownWithCallback(). - \param[in] label The name of the dropdown menu. - \param[in] values A list of options to show in the dropdown menu. - \param[in] var A reference to a user variable that will be updated directly when a dropdown option changes. This correlates to the 'pValue' field in Gui#SDropdownValue struct. - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - \return true if the value changed, otherwise false - */ - bool addDropdown(const char label[], const DropdownList& values, uint32_t& var, bool sameLine = false); - - /** Render a tooltip. This will display a small question mark next to the last label item rendered and will display the tooltip if the user hover over it - \param[in] tip The tooltip's text - \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget - */ - void addTooltip(const char tip[], bool sameLine = true); - - /** Adds a text box. - \param[in] label The name of the variable. - \param[in] buf A character buffer with the initialize text. The buffer will be updated if a text is entered. - \param[in] bufSize The size of the text buffer - \param[in] lineCount Number of lines in the text-box. If larger then 1 will create a multi-line box - \return true if the value changed, otherwise false - */ - bool addTextBox(const char label[], char buf[], size_t bufSize, uint32_t lineCount = 1); - - /** Adds a text box. - \param[in] label The name of the variable. - \param[in] text A string with the initialize text. The string will be updated if a text is entered. - \param[in] lineCount Number of lines in the text-box. If larger then 1 will create a multi-line box - \return true if the value changed, otherwise false - */ - bool addTextBox(const char label[], std::string& text, uint32_t lineCount = 1); - - /** Adds multiple text boxes for one confirmation button - * - */ - bool addMultiTextBox(const char label[], const std::vector& textLabels, std::vector& textEntries); - - using GraphCallback = float(*)(void*, int32_t index); - - /** Adds a graph based on a function - \param[in] label The name of the widget. - \param[in] func A function pointer to calculate the values in the graph - \param[in] pUserData A user-data pointer to pass to the callback function - \param[in] sampleCount Number of sample-points in the graph - \param[in] sampleOffset Optional. Determines the value for the center of the x-axis - \param[in] yMin Optional. The minimum value of the y-axis. Use FLT_MAX to auto-detect the range based on the function and the provided x-range - \param[in] yMax Optional. The maximum value of the y-axis. Use FLT_MAX to auto-detect the range based on the function and the provided x-range - \param[in] width Optional. The width of the graph widget. 0 means auto-detect (fits the widget to the GUI width) - \param[in] height Optional. The height of the graph widget. 0 means auto-detect (no idea what's the logic. Too short.) - */ - void addGraph(const char label[], GraphCallback func, void* pUserData, uint32_t sampleCount, int32_t sampleOffset, float yMin = FLT_MAX, float yMax = FLT_MAX, uint32_t width = 0, uint32_t height = 100); - - /** Adds a direction widget - \param[in] label The name of the widget. - \param[in] direction A reference for the direction variable - \return true if the value changed, otherwise false - */ - bool addDirectionWidget(const char label[], glm::vec3& direction); - - /** Set global font size scaling - */ - static void setGlobalGuiScaling(float scale); - - /** Create a new window on the stack - */ - void pushWindow(const char label[], uint32_t width = 0, uint32_t height = 0, uint32_t x = 0, uint32_t y = 0, bool showTitleBar = true, bool allowMove = true, bool focus = true, bool allowClose = true); - - /** End a window block - */ - void popWindow(); - - /** Set the current window position in pixels - \param[in] x horizontal window position in pixels - \param[in] y vertical window position in pixels - */ - void setCurrentWindowPos(uint32_t x, uint32_t y); - - /** Get the position of the current window. - \return vec2 Value of window position - */ - glm::vec2 getCurrentWindowPos(); - - /** Set the size of the current window in pixels - \param[in] width Window width in pixels - \param[in] height Window height in pixels - */ - void setCurrentWindowSize(uint32_t width, uint32_t height); - - /** Get the size of the current window in pixels. - \return vec2 Value of window size - */ - glm::vec2 getCurrentWindowSize(); - - /** Get if the window with the given label is currently open in the ui - */ - bool isWindowOpen(const char label[]); - - /** - */ - void setWindowOpen(const char label[], bool isOpen); - - /** Start a new frame. Must be called at the start of each frame - */ - void beginFrame(); - - /** Add a font - */ - void addFont(const std::string& name, const std::string& filename); - - /** Set the active font - */ - void setActiveFont(const std::string& font); - - private: - Gui() = default; - void init(float scaleFactor); - void createVao(uint32_t vertexCount, uint32_t indexCount); - void compileFonts(); - - // Helper to create multiple inline text boxes - bool addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine); - - struct ComboData - { - uint32_t lastVal = -1; - int32_t currentItem = -1; - }; - std::map mDropDownValues; - - // This struct is used to cache the mouse events - struct MouseEvents - { - bool buttonPressed[3] = { 0 }; - bool buttonReleased[3] = { 0 }; - }; - - MouseEvents mMouseEvents; - void setIoMouseEvents(); - void resetMouseEvents(); - - Vao::SharedPtr mpVao; - VertexLayout::SharedPtr mpLayout; - GraphicsVars::SharedPtr mpProgramVars; - GraphicsProgram::SharedPtr mpProgram; - GraphicsState::SharedPtr mpPipelineState; - uint32_t mGroupStackSize = 0; - - std::vector mpImages; - std::unordered_map mOpenWindows; - std::vector mGroupHasWindow; - ParameterBlockReflection::BindLocation mGuiImageLoc; - float mScaleFactor = 1.0f; - std::unordered_map mFontMap; - ImFont* mpActiveFont = nullptr; - }; -} diff --git a/Framework/Source/Utils/Logger.cpp b/Framework/Source/Utils/Logger.cpp deleted file mode 100644 index e72d09c8c..000000000 --- a/Framework/Source/Utils/Logger.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Logger.h" -#include "Utils/Platform/OS.h" -#include - -namespace Falcor -{ - dlldecl Logger::Data gLoggerData; - - static FILE* openLogFile() - { - FILE* pFile = nullptr; - - // Get current process name - std::string filename = getExecutableName(); - - // Now we have a folder and a filename, look for an available filename (we don't overwrite existing files) - std::string prefix = std::string(filename); - std::string executableDir = getExecutableDirectory(); - std::string logFile; - if(findAvailableFilename(prefix, executableDir, "log", logFile)) - { - pFile = std::fopen(logFile.c_str(), "w"); - if(pFile != nullptr) - { - // Success - return pFile; - } - } - // If we got here, we couldn't create a log file - should_not_get_here(); - return pFile; - } - - bool Logger::initialize() - { - if (gLoggerData.initialized == false) - { - gLoggerData.pLogFile = openLogFile(); - gLoggerData.initialized = gLoggerData.pLogFile != nullptr; - assert(gLoggerData.initialized); - } - return true; - } - - void Logger::shutdown() - { - if (gLoggerData.initialized) - { - fclose(gLoggerData.pLogFile); - gLoggerData.pLogFile = nullptr; - gLoggerData.initialized = false; - } - } - - void Logger::showBoxOnError(bool showBox) - { - gLoggerData.showErrorBox = showBox; - } - - bool Logger::isBoxShownOnError() - { - return gLoggerData.showErrorBox; - } - - void Logger::setVerbosity(Level level) - { - gLoggerData.verbosity = level; - } - - const char* getLogLevelString(Logger::Level L) - { - const char* c = nullptr; -#define create_level_case(_l) case _l: c = "(" #_l ")" ;break; - switch(L) - { - create_level_case(Logger::Level::Info); - create_level_case(Logger::Level::Warning); - create_level_case(Logger::Level::Error); - default: - should_not_get_here(); - } -#undef create_level_case - return c; - } - - void Logger::log(Level L, const std::string& msg, bool forceMsgBox) - { -#if _LOG_ENABLED - if(gLoggerData.initialized) - { - if(L >= gLoggerData.verbosity) - { - std::string s = getLogLevelString(L) + std::string("\t") + msg + "\n"; - std::fprintf(gLoggerData.pLogFile, "%s", s.c_str()); - fflush(gLoggerData.pLogFile); // Slows down execution, but ensures that the message will be printed in case of a crash - if (isDebuggerPresent()) - { - printToDebugWindow(s); - } - } - } -#endif - - if(L >= Level::Error) - { - if(isDebuggerPresent()) - { - debugBreak(); - } - - forceMsgBox = gLoggerData.showErrorBox; - } - - if (forceMsgBox) - { - msgBox(msg); - } - - if (L >= Level::Fatal) assert(false); // PETRIK: Assert on errors even without debugger attached - } -} \ No newline at end of file diff --git a/Framework/Source/Utils/PythonEmbedding.cpp b/Framework/Source/Utils/PythonEmbedding.cpp deleted file mode 100644 index 26dc97a39..000000000 --- a/Framework/Source/Utils/PythonEmbedding.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "FalcorConfig.h" - -#if FALCOR_USE_PYTHON - -#include "Python.h" -#include "PythonEmbedding.h" -#include -#include - -// Requires use of pybind11. See README.txt in sample "LearningWithEmbeddedPython" for more info -namespace py = pybind11; -using namespace py::literals; - -// If using this fragine sharing scheme, create a global variable for the interpreter that everyone can reuse -#ifdef PYTHON_USE_SIMPLE_SHARING -namespace { - py::scoped_interpreter* gPython = nullptr; -}; -#endif - - -PythonEmbedding::PythonEmbedding(bool redirectStdout) : - mRedirectStdout(redirectStdout) -{ - bool doInit = true; - -#ifdef PYTHON_USE_SIMPLE_SHARING - // Create a new interpreter embedding - if (!gPython) - { - gPython = new py::scoped_interpreter(); - } - else - { - doInit = false; - } - mpInterp = gPython; -#else - // Create a new interpreter embedding - if (!mpInterp) - { - mpInterp = new py::scoped_interpreter(); - } - else - { - doInit = false; - } -#endif - - // Create a __main__ module for Python to run in - mMainModule = py::module::import("__main__"); - - // Grab a pointer to the dictionary used to store Python's global variables - mGlobals = py::reinterpret_borrow(mMainModule.attr("__dict__").ptr()); - - // We need to call PySys_SetArgvEx() to avoid errors when querying/reflecting on the Python interpreter. - // In theory, all this does is set the relative path to a script that is being run. Since an embedded - // interpreter has no script running on startup, this is NULL. This looks like a bunch of no-ops, but - // it's vital to avoid crazy embedded Python errors. - if (doInit) - { - size_t size = 0; - wchar_t* argv = Py_DecodeLocale("", &size); - PySys_SetArgvEx(0, &argv, 0); // 3rd zero essentially for using Python as ongoing interpreter - PyMem_RawFree(argv); - } - - // Get a stringified representation of the version of Python embedded. - // -> Grab the value stored in 'sys.version' - // -> This is often a huge string with compiler name and build time; grab just until the first space. - mPythonVersion = std::string(py::handle(py::module::import("sys").attr("version").ptr()).cast()); - size_t offset = mPythonVersion.find_first_of(' '); - mPythonVersion = mPythonVersion.substr(0, offset); - - // Insert this main module into our list of modules - mModuleList["__main__"] = std::make_pair(mMainModule,std::string("__main__")); - - // When redirecting output, there's some setup that needs to happen. Since the default behavior of this - // class is to start redirecting stdout without any user intervention, we need to actually call the - // hooks to start redirecting inside the constructor. Do that here. It checks to make sure all - // relevant state has been initialized appropriately. - if (mRedirectStdout || mRedirectStderr) - { - PythonEmbedding::redirectStdout(mRedirectStdout); // Whoops. Method hidden by constructor param! :-/ - } - -} - -PythonEmbedding::~PythonEmbedding() -{ - // TODO: Need to figure out how to shutdown appropriately - /* - if (mpInterp) - { - delete mpInterp; - mpInterp = nullptr; - } - */ -} - -std::string PythonEmbedding::getPythonVersion(void) -{ - return mPythonVersion; -} - -std::string PythonEmbedding::getModuleVersion(const char* moduleName, const char* attrName) -{ - // Check. Have we loaded the specified module? - std::string strName = std::string(moduleName); - auto found = mModuleList.find(strName); - - // Wasn't found in our list of loaded modules - if (found == mModuleList.end()) - { - return std::string(""); - } - - // The handle/name pair for our found module - auto foundPair = found->second; - py::handle foundHandle = foundPair.first; - - // We tried to load the module.... and failed - if (foundHandle.ptr() == nullptr) - { - return std::string(""); - } - - // Get the version number - PyObject* pVerAttrib = PyObject_GetAttrString(foundHandle.ptr(), attrName); - - // The specified AttrString was invalid, so we don't know the version number! - if (!pVerAttrib) - { - return std::string(""); - } - - // We got a version ID!! - return std::string(py::handle(pVerAttrib).cast()); -} - -bool PythonEmbedding::importModule(const char* moduleName, const char* importAs) -{ - bool success = false; - - // Check. Have we already loaded this module? - std::string strName = std::string(moduleName); - auto found = mModuleList.find(strName); - - // We found an existing entry for this module - if (found != mModuleList.end()) - { - // Check to see if the stored py::handle is non-null. If so, we've already imported the module. - auto second = found->second; - if (second.first.ptr() != nullptr) - { - return true; - } - - // If we found the module, but hadn't imported successfully, might as well try again... - } - - // Import the module - PyObject* pLibModule = PyImport_ImportModule(moduleName); - const char* importName = (importAs == nullptr) ? moduleName : importAs; - if (pLibModule) - { - int error = PyModule_AddObject(mMainModule.ptr(), importName, pLibModule); - if (!error) - { - success = true; - } - } - - // Insert this module into our list of modules we've loaded, to avoid reloading in the future. - mModuleList[strName] = std::make_pair(py::handle(success ? pLibModule : nullptr), - std::string(importName)); - - return success; -} - -bool PythonEmbedding::fromModuleImport(const char* moduleName, const char* toImport, const char* importAs) -{ - bool success = false; - - // Check. Have we already loaded this object? (Could be a module, could be a class, could be a function) - std::string strName = std::string(moduleName) + std::string(".") + std::string(toImport); - auto found = mModuleList.find(strName); - - // We found an existing entry for this object - if (found != mModuleList.end()) - { - // Check to see if the stored py::handle is non-null. If so, we've already imported the module. - auto second = found->second; - if (second.first.ptr() != nullptr) - { - return true; - } - - // If we found it, but havn't imported successfully, might as well try again... - } - - // Import the requested object - py::str tmpName = py::str(toImport); - const char* importName = (importAs == nullptr) ? toImport : importAs; - PyObject* pSubModules = PyList_New(0); - PyList_Append(pSubModules, tmpName.ptr() ); - PyObject* pLibImports = PyImport_ImportModuleEx(moduleName, NULL, NULL, pSubModules); - PyObject* pObjToImport = NULL; - if (pLibImports) - { - pObjToImport = PyObject_GetAttr(pLibImports, tmpName.ptr()); - if (pObjToImport) - { - int error = PyModule_AddObject(mMainModule.ptr(), importName, pObjToImport ); - if (!error) - { - success = true; - } - } - } - - // Insert this object into our list of things we've loaded, to avoid reloading in the future. - mModuleList[strName] = std::make_pair(py::handle(success ? pObjToImport : nullptr), - std::string(importName)); - - return success; -} - -pybind11::detail::item_accessor PythonEmbedding::operator[] (const char* globalVarName) -{ - return mGlobals[globalVarName]; -} - -pybind11::detail::item_accessor PythonEmbedding::operator[] (const std::string &globalVarName) -{ - return this->operator[](globalVarName.c_str()); -} - -pybind11::dict PythonEmbedding::getGlobals(void) -{ - return mGlobals; -} - -bool PythonEmbedding::doesGlobalVarExist(const char* globalVarName) -{ - return mGlobals.contains(py::str(std::string(globalVarName))); -} -bool PythonEmbedding::doesGlobalVarExist(const std::string &globalVarName) -{ - return mGlobals.contains(py::str(globalVarName)); -} - -bool PythonEmbedding::executeFile(const std::string &scriptFile) -{ - return executeFile(scriptFile.c_str()); -} - -std::string PythonEmbedding::getError(void) -{ - return mLastPythonError; -} - -std::string PythonEmbedding::getStdout(void) -{ - return mLastPythonStdout; -} - -std::string PythonEmbedding::getStderr(void) -{ - return mLastPythonStderr; -} - -void PythonEmbedding::clearPythonState(void) -{ - PyErr_Clear(); - mLastPythonError = std::string(""); - mLastPythonStdout = std::string(""); - mLastPythonStderr = std::string(""); - mLastCostPython = -1.0; - mLastCostTotal = -1.0; -} - -bool PythonEmbedding::executeFile(const char* scriptFile) -{ - return commonExecRoutine(py::str(scriptFile), false); -} - -bool PythonEmbedding::executeString(const char* scriptCode) -{ - if (!scriptCode) return false; - if (scriptCode[0] != '\n') - { - return commonExecRoutine(py::str(scriptCode), true); - } - else - { - return commonExecRoutine(py::str(py::module::import("textwrap").attr("dedent")(scriptCode)), true); - } -} - -bool PythonEmbedding::executeString(const std::string &scriptCode) -{ - return commonExecRoutine(py::str(scriptCode.c_str()), true); -} - -bool PythonEmbedding::executeFile(const char* scriptFile, pybind11::dict localNamespace) -{ - return commonExecRoutine(py::str(scriptFile), false, true, localNamespace); -} - -bool PythonEmbedding::executeFile(const std::string &scriptFile, pybind11::dict localNamespace) -{ - return commonExecRoutine(py::str(scriptFile.c_str()), false, true, localNamespace); -} - -bool PythonEmbedding::executeString(const char* scriptCode, pybind11::dict localNamespace) -{ - return commonExecRoutine(py::str(scriptCode), true, true, localNamespace); -} - -bool PythonEmbedding::executeString(const std::string &scriptCode, pybind11::dict localNamespace) -{ - return commonExecRoutine(py::str(scriptCode.c_str()), true, true, localNamespace); -} - -bool PythonEmbedding::commonExecRoutine(const pybind11::str &input, bool asString, bool useLocals, pybind11::object locals) -{ - bool success = false; - clearPythonState(); - std::chrono::high_resolution_clock::time_point t0, t1, t2, t3; - t0 = std::chrono::high_resolution_clock::now(); - - try - { - t1 = std::chrono::high_resolution_clock::now(); - if (asString) // Treat the input as Python command(s) to execute - { - py::exec(input, mGlobals, locals); - } - else // Treat the input as a file. Load and run the code in that file. - { - py::eval_file(input, mGlobals, locals); - } - t2 = std::chrono::high_resolution_clock::now(); - - success = true; - } - catch (pybind11::error_already_set &e) - { - mLastPythonError = std::string(e.what()); - } - catch (...) - { - mLastPythonError = std::string(""); - } - - getRedirectedOutputs(); - t3 = std::chrono::high_resolution_clock::now(); - - // Compute costs for the execution. If no success, leave as defaults (-1 ms) - if (success) - { - std::chrono::duration spanTotal = t3 - t0; - std::chrono::duration spanPython = t2 - t1; - mLastCostTotal = double(spanTotal.count()); - mLastCostPython = double(spanPython.count()); - } - - return success; -} - -double PythonEmbedding::lastExecutionTime(bool totalTime) -{ - return totalTime ? mLastCostTotal : mLastCostPython; -} - -void PythonEmbedding::checkRedirectionImports(void) -{ - // Redirection requires the 'io' library (we use io.StringIO()) - auto found = mModuleList.find("io"); - if (found == mModuleList.end()) - { - importModule("io"); - } - - // Redirection requires the 'sys' library (to access/change str.stdout) - found = mModuleList.find("sys"); - if (found == mModuleList.end()) - { - importModule("sys"); - } -} - -void PythonEmbedding::redirectStdout(bool redirect) -{ - // If we're enabling redirection, ensure Python's io and sys modules are loaded. - if (redirect) - { - checkRedirectionImports(); - } - if (mRedirectValid) - { - stopRedirectingOutputs(); - } - - // Enable/disable redirection - mRedirectStdout = redirect; - if (redirect) - { - startRedirectingOutputs(); - } -} - -void PythonEmbedding::redirectStderr(bool redirect) -{ - // If we're enabling redirection, ensure Python's io and sys modules are loaded. - if (redirect) - { - checkRedirectionImports(); - } - if (mRedirectValid) - { - stopRedirectingOutputs(); - } - - // Enable/disable redirection - mRedirectStderr = redirect; - if (redirect) - { - startRedirectingOutputs(); - } -} - -// This is fairly simplistic and not necessarily entirely robust. -// Create a StringIO() object; redirect stdout to that via a hardcoded -// redirect (i.e., assigning sys.stdout to our new object). A context -// manager would be "nice" and handle Python exceptions cleanly, but -// would cause embedded Python code to execute in a different scope; this -// likely would cause all sorts of unintended results/errors. -// This does pollute Python's global namespace somewhat, but I named the -// resources quite explicitly to avoid duplicate names. -// I've attempted to make failure of this redirection have no externally visible -// consequences... except that redirection will not occur. -void PythonEmbedding::startRedirectingOutputs(void) -{ - // We call this every time we execute; if we don't need it, return. - if (!mRedirectStdout && !mRedirectStderr) return; - - PyErr_Clear(); - bool success = false; - try { - // Probably should combine into a single string with one py::exec() call... Would save a bit of overhead. - if (mRedirectStdout) - { - py::exec("_fPyEmb_redirectStdout = io.StringIO(); sys.stdout = _fPyEmb_redirectStdout", mGlobals); - } - if (mRedirectStderr) - { - py::exec("_fPyEmb_redirectStderr = io.StringIO(); sys.stderr = _fPyEmb_redirectStderr", mGlobals); - } - - success = true; - } catch (...) { ; } - - // If this function was successful, we've successfully redirected those outputs requested. - // -> While failure here does not guarantee we have no successful redirection currently ongoing, - // we will be unable to guarantee that everything guarded by mRedirectValid will execute - // without exception, so we need to be conservative. Fortunately, stopping redirection - // shouldn't need protection by this flag. - mRedirectValid = success; - - PyErr_Clear(); -} - -// In the model where start/stop redirecting outputs are called only upon -// toggling the redirection, we need a method to grab the current stdio/stderr -// streams to our internal class structures. This code also optionally clears -// the Python streams (the default behavior) so that repeated calls will not -// return only the incremental, new outputs rather than a cumulative history. -void PythonEmbedding::getRedirectedOutputs(bool clearBuffers) -{ - // We call this every time we execute; if we don't need it, return. - if (!mRedirectStdout && !mRedirectStderr) - { - return; - } - - // If we want to redirect, but we failed to begin redirecting, we need to abort. - if (!mRedirectValid) - { - return; - } - - PyErr_Clear(); - bool success = false; - try { - std::string stdoutGrabber = clearBuffers ? - std::string("_fPyEmb_redirectStdoutResult = _fPyEmb_redirectStdout.getvalue(); _fPyEmb_redirectStdout.seek(0); _fPyEmb_redirectStdout.truncate(0)") : - std::string("_fPyEmb_redirectStdoutResult = _fPyEmb_redirectStdout.getvalue()"); - std::string stderrGrabber = clearBuffers ? - std::string("_fPyEmb_redirectStderrResult = _fPyEmb_redirectStderr.getvalue(); _fPyEmb_redirectStderr.seek(0); _fPyEmb_redirectStderr.truncate(0)") : - std::string("_fPyEmb_redirectStderrResult = _fPyEmb_redirectStderr.getvalue()"); - - if (mRedirectStdout) - { - py::exec(py::str(stdoutGrabber), mGlobals); - } - if (mRedirectStderr) - { - py::exec(py::str(stderrGrabber), mGlobals); - } - - success = true; - } catch (...) { ; } - - if (success) - { - if (mRedirectStdout) - { - mLastPythonStdout = std::string(mGlobals["_fPyEmb_redirectStdoutResult"].cast()); - } - if (mRedirectStderr) - { - mLastPythonStderr = std::string(mGlobals["_fPyEmb_redirectStderrResult"].cast()); - } - } - - PyErr_Clear(); -} - -// This undoes what occurs in startRedirectingStdout() and (possibly) dumps any remaining -// redirected data to a C++ std::string. Again, if designed correctly, failure should -// have no externally visible consequences, except a lack of redirection. -void PythonEmbedding::stopRedirectingOutputs(bool getOutputs) -{ - // If we have no valid redirection ongoing, we could simply return. But in order - // to give consistent behavior under the maximum # of scenarios, let's execute - // our Python to reset stdout and stderr back to their default. But because - // we may have no valid redirection, we can't guarantee the existance of the - // redirection variables (e.g., _fPyEmb_redirectStdout), so avoid getting outputs - if (!mRedirectValid) - { - getOutputs = false; - } - - // If we call stopRedirectingOutput(), treat everything afterwards as if it - // has invalid redirection (even if we return early below or catch an exception). - mRedirectValid = false; - - // We call this every time we execute; if we don't need it, return. - if (!mRedirectStdout && !mRedirectStderr) - { - return; - } - - PyErr_Clear(); - - bool success = false; - try { - std::string stdoutGrabber = getOutputs ? - std::string("sys.stdout = sys.__stdout__; _fPyEmb_redirectStdoutResult = _fPyEmb_redirectStdout.getvalue()") : - std::string("sys.stdout = sys.__stdout__"); - std::string stderrGrabber = getOutputs ? - std::string("sys.stderr = sys.__stderr__; _fPyEmb_redirectStderrResult = _fPyEmb_redirectStderr.getvalue()") : - std::string("sys.stderr = sys.__stderr__"); - - if (mRedirectStdout) - { - py::exec(py::str(stdoutGrabber), mGlobals); - } - if (mRedirectStderr) - { - py::exec(py::str(stderrGrabber), mGlobals); - } - - success = true; - } catch (...) { ; } - - if (success && getOutputs) - { - if (mRedirectStdout) - { - mLastPythonStdout = std::string(mGlobals["_fPyEmb_redirectStdoutResult"].cast()); - } - if (mRedirectStderr) - { - mLastPythonStderr = std::string(mGlobals["_fPyEmb_redirectStderrResult"].cast()); - } - } - - // Clear errors after converting mGlobals[] back to strings, since conversions can throw PyErrors too! - PyErr_Clear(); -} - -#endif \ No newline at end of file diff --git a/Framework/Source/Utils/PythonEmbedding.h b/Framework/Source/Utils/PythonEmbedding.h deleted file mode 100644 index 9531a4d6e..000000000 --- a/Framework/Source/Utils/PythonEmbedding.h +++ /dev/null @@ -1,223 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -// Please note: This is an early, hacked version of Python embedding that we're distributing -// due to requests from multiple folks who heard our SIGGRAPH 2017 presentation -// using a demo built on this code. Not promised to be production ready! -// -// See the README.txt in the Falcor Sample "LearningWithEmbeddedPython" for more info on how -// to use this file and what steps are needed to set up Python. - -#pragma once - -#include "FalcorConfig.h" - -#if FALCOR_USE_PYTHON - -#include - -/** Include to handle C++/Python type conversions. - Used internally to simplify Python embedding. (Library is BSD licensed) - Grab from here: https://github.com/pybind/pybind11 -*/ -#include "pybind11/pybind11.h" -#include "pybind11/embed.h" -#include "pybind11/eval.h" - -/** If PYTHON_USE_SIMPLE_SHARING is defined, use a simplistic and fragile approach that - allows usage of multiple PythonEmbedding class instantiations simultaneously (but - all using a single Python interpreter and shared global namespace). If not - defined, the use of multiple PythonEmbedding objects simultaneously will lead to - unknown/undefined results (likely random crashes). - TODO: Sharing Python embeddings clearly needs some more work! -*/ -//#define PYTHON_USE_SIMPLE_SHARING - -/** Class for easily embedding Python into C++ code. - IMPORTANT NOTES on current usage assumptions: - 1) The Python embedding used assumes it is the *only* embedded interpreter. - -> Using multiple class instantiations may cause undefined behavior. - -> See 'PYTHON_USE_SIMPLE_SHARING' for a possible, very fragile solution - 2) Class designed to initialize once, use for life of program, destroy once. - -> Multiple creation/deletion cycles not extensively tested - 3) Anyways... deletion of Python interpreter fails and is currently disabled. - -> For now, treat this class as a create-once resource. -*/ -class PythonEmbedding : public std::enable_shared_from_this -{ -public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - /** Constructors & destructors - */ - PythonEmbedding(bool redirectStdout = true); - virtual ~PythonEmbedding(); - static SharedPtr create(bool redirectStdout = true) { return std::make_shared(redirectStdout); } - - /** Methods to globally import Python modules for later use. Note this can be done directly in Python, - however doing it with these methods allows you to easily query module version information, pay - module startup costs at an appropriate time, etc. - -> Python: "import numpy" -> importModule( "numpy" ); - -> Python: "import numpy as np" -> importModule( "numpy", "np" ); - -> Python: "from hashlib import md5" -> fromModuleImport( "hashlib", "md5" ) - -> Python: "from hashlib import md5 as myname" -> fromModuleImport( "hashlib", "md5", "myname" ) - \return true on success, false on failure. - */ - bool importModule(const char *moduleName, const char *importAs = nullptr); - bool fromModuleImport(const char *moduleName, const char *toImport, const char *importAs = nullptr); - - /** Accessors for variables in the global namespace. - -> These can be assigned directly, if you want to send data to Python (e.g., this->operator[]("myVar") = 45; ) - This has pybind11 do the conversion behind your back. It usually works pretty well, for simple types. - You can also explicitly convert to Python/pybind11 object types and pass those on the right hand side, - for more complex data types. - -> These can be used to access Python variables (e.g., int( this->operator[]("myVar").cast() ); ) - Casting to C++ this way is, as you may notice, a lot more painful. Oh well. One day I hope to clean that up. - -> If the variable does not exist, casting it via pybind11's cast() will throw an exception. - Before casting, you can/should check if a variable exists with: doesGlobalVarExist(globalVarName) - -> These accessors are equivalent to calling: getGlobals()[globalVarName] - */ - pybind11::detail::item_accessor operator[] (const char* globalVarName); - pybind11::detail::item_accessor operator[] (const std::string &globalVarName); - - /** Check if a global variable exists - */ - bool doesGlobalVarExist(const char *globalVarName); - bool doesGlobalVarExist(const std::string &globalVarName); - - /** Return the entire dictionary of Python's global variables - */ - pybind11::dict getGlobals(void); - - /** Return the last Python error encountered during executeFile() or executeString() - */ - std::string getError(void); - - /** If redirecting stdout and/or stderr is enabled, the appropriate functions will get you a string - with the outputs of stdout/stderr from your *last* fall to executeFile() or executeString(). - */ - std::string getStdout(void); - std::string getStderr(void); - - /** Determine if stdout/stderr will be redirected when executing Python via this class. - -> Note that the settings are independent for stdout and stderr (you can redirect one, but not the other) - -> Note: method defaults parameters are different than class defaults. (redirectStderr() implies "make it so") - -> Redirecting outputs introduces overheads of around 0.1 ms per execute() call, for a few lines of output. - */ - void redirectStdout(bool redirect = true); - void redirectStderr(bool redirect = true); - - /** Returns a string representing the version of Python. - */ - std::string getPythonVersion(void); - - /** Returns a string representing the version number of the specified module... if it has successfully been loaded. - -> The string returned is that stored in attrName (e.g., "numpy.__version__" in Python returns NumPy's version) - -> Returns the string "" if a module is not loaded - -> Returns "" if a module is loaded but the specified attrName does not exist - */ - std::string getModuleVersion(const char *moduleName, const char *attrName = "__version__"); - - /** Methods to execute Python from a specified file/string in the global namespace - -> Success returns true. Failure returns false, errors available via getPythonErrors() - -> Multiline strings need to be specified with a C++ 11 raw string literal, e.g., R"( ... )" - in order to allow explicit newlines and tabs to be passed to the Python interpreter for - correct understanding of code indentation. - */ - bool executeFile(const char *scriptFile); - bool executeFile(const std::string &scriptFile); - bool executeString(const char *scriptCode); - bool executeString(const std::string &scriptCode); - - /** Methods to execute Python from a specified file/string in a new, local namespace - -> Important note: Unless you really know what you're doing, this is *rarely* - what you want to do. It screws up variable scoping in a way you likely will - not expect. This behaves _exactly_ like calling exec() from within Python, - so read this: https://docs.python.org/3/library/functions.html?highlight=exec#exec - -> Note in that URL, the description of these functions: - "code will be executed as if [the whole script] were embedded in a class definition" - -> Success returns true. Failure returns false, errors available via getPythonErrors() - */ - bool executeFile(const char *scriptFile, pybind11::dict localNamespace); - bool executeFile(const std::string &scriptFile, pybind11::dict localNamespace); - bool executeString(const char *scriptCode, pybind11::dict localNamespace); - bool executeString(const std::string &scriptCode, pybind11::dict localNamespace); - - /** Returns the cost of the last executeFile() or executeString() call in milliseconds - (to the precision allowed by std::chrono::high_resolution_clock). If the last - execution threw an exception, the return value is -1. - -> if totalTime is true, returns the cost of the execute*() call - -> it totalTime is false, returns the cost of executing just the specified Python - code, without setup overheads introduced by this class. - */ - double lastExecutionTime( bool totalTime = true ); - -private: - // Internal member variables - - pybind11::scoped_interpreter *mpInterp = nullptr; // The C++ embedded Python interpreter - pybind11::module mMainModule; // Stores the Python module __main__ - pybind11::dict mGlobals; // A dictionary of the global Python variables - std::string mPythonVersion; // A string representation of the embedded Python's version - std::string mLastPythonError = ""; // A string storing the last Python error (from an exception) - - bool mRedirectStdout; // Redirect stdout of executions to mLastPythonStdout? (initialized in constructor) - std::string mLastPythonStdout = ""; // A string storing the last execution's stdout output (if requested) - bool mRedirectStderr = false; // Redirect stderr of executions to mLastPythonStderr? - std::string mLastPythonStderr = ""; // A string storing the last execution's stderr output (if requested) - bool mRedirectValid = false; // An internal status check; was redirection successful? - - double mLastCostTotal = -1; // The cost of the last executeFile()/executeString() call - double mLastCostPython = -1; // The cost of the Python code from the last executeFile()/executeString() call - - /** Stores a list of modules we've tried to load. - */ - std::map< std::string,std::pair > mModuleList; - - /** When executing a new command/file, clear existing error, I/o, etc. state - */ - void clearPythonState(void); - - /** Internal method to ensure our interpreter has the necessary imports for redirecting stdout/stderr - */ - void checkRedirectionImports(void); - - /** Internal methods to start or stop redirecting stdout and/or stderr to strings - */ - void startRedirectingOutputs(void); - void stopRedirectingOutputs(bool getOutputs = false); - void getRedirectedOutputs(bool clearBuffers = true); - - /** A common execution routine that handles all execute*() routines above to keep exection code localized in one spot - */ - bool commonExecRoutine(const pybind11::str &input, bool asString, bool useLocals = false, pybind11::object locals = class pybind11::object()); - -}; - -#endif \ No newline at end of file diff --git a/Framework/Source/Utils/Scripting/ScriptBindings.cpp b/Framework/Source/Utils/Scripting/ScriptBindings.cpp deleted file mode 100644 index 05d1f209d..000000000 --- a/Framework/Source/Utils/Scripting/ScriptBindings.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "ScriptBindings.h" -#include "Scripting.h" -#include "API/Sampler.h" -#include "Effects/ToneMapping/ToneMapping.h" -#include "Graphics/Scene/Scene.h" -#ifdef FALCOR_D3D12 -#include "Experimental/Raytracing/RtScene.h" -#endif -#include "Experimental/RenderGraph/RenderGraph.h" - -using namespace pybind11::literals; - -namespace Falcor -{ - const char* ScriptBindings::kLoadScene = "loadScene"; - const char* ScriptBindings::kLoadRtScene = "loadRtScene"; - -#define val(a) value(to_string(a).c_str(), a) - - static void globalEnums(pybind11::module& m) - { - // Resource formats - auto formats = pybind11::enum_(m, "Format"); - for (uint32_t i = 0; i < (uint32_t)ResourceFormat::Count; i++) - { - formats.val(ResourceFormat(i)); - } - - // Comparison mode - auto comparison = pybind11::enum_(m, "Comparison"); - comparison.val(ComparisonFunc::Disabled).val(ComparisonFunc::LessEqual).val(ComparisonFunc::GreaterEqual).val(ComparisonFunc::Less).val(ComparisonFunc::Greater); - comparison.val(ComparisonFunc::Equal).val(ComparisonFunc::NotEqual).val(ComparisonFunc::Always).val(ComparisonFunc::Never); - } - - static void samplerState(pybind11::module& m) - { - auto filter = pybind11::enum_(m, "Filter"); - filter.val(Sampler::Filter::Linear).val(Sampler::Filter::Point); - - auto addressing = pybind11::enum_(m, "AddressMode"); - addressing.val(Sampler::AddressMode::Wrap).val(Sampler::AddressMode::Mirror).val(Sampler::AddressMode::Clamp).val(Sampler::AddressMode::Border).val(Sampler::AddressMode::MirrorOnce); - } - - static void toneMapping(pybind11::module& m) - { - auto op = pybind11::enum_(m, "ToneMapOp"); - op.val(ToneMapping::Operator::Clamp).val(ToneMapping::Operator::Linear).val(ToneMapping::Operator::Reinhard).val(ToneMapping::Operator::ReinhardModified).val(ToneMapping::Operator::HejiHableAlu); - op.val(ToneMapping::Operator::HableUc2).val(ToneMapping::Operator::Aces); - } - - static void scene(pybind11::module& m) - { - // Model load flags - auto model = pybind11::enum_(m, "ModelLoadFlags"); - model.val(Model::LoadFlags::None).val(Model::LoadFlags::DontGenerateTangentSpace).val(Model::LoadFlags::FindDegeneratePrimitives).val(Model::LoadFlags::AssumeLinearSpaceTextures); - model.val(Model::LoadFlags::DontMergeMeshes).val(Model::LoadFlags::BuffersAsShaderResource).val(Model::LoadFlags::RemoveInstancing).val(Model::LoadFlags::UseSpecGlossMaterials); - - // Scene load flags - auto scene = pybind11::enum_(m, "SceneLoadFlags"); - scene.val(Scene::LoadFlags::None).val(Scene::LoadFlags::GenerateAreaLights); - - // Scene - m.def(ScriptBindings::kLoadScene, &Scene::loadFromFile, "filename"_a, "modelLoadFlags"_a = Model::LoadFlags::None, "sceneLoadFlags"_a = Scene::LoadFlags::None); - auto sceneClass = pybind11::class_(m, "Scene"); - - // RtScene -#ifdef FALCOR_D3D12 - // RtSceneFlags - auto rtScene = pybind11::enum_(m, "RtBuildFlags"); - rtScene.val(RtBuildFlags::None).val(RtBuildFlags::AllowUpdate).val(RtBuildFlags::AllowCompaction).val(RtBuildFlags::FastTrace).val(RtBuildFlags::FastBuild); - rtScene.val(RtBuildFlags::MinimizeMemory).val(RtBuildFlags::PerformUpdate); - - auto rtSceneClass = pybind11::class_(m, "RtScene", sceneClass); - m.def(ScriptBindings::kLoadRtScene, &RtScene::loadFromFile, "filename"_a, "rtBuildFlags"_a = RtBuildFlags::None, "modelLoadFlags"_a = Model::LoadFlags::None, "sceneLoadFlags"_a = Scene::LoadFlags::None); -#endif - } - - static void coreClasses(pybind11::module& m) - { -#define reg_class(c_) pybind11::class_(m, #c_); - - // API - reg_class(BlendState); - reg_class(Buffer); -// reg_class(ConstantBuffer); Doesn't work since it uses user-defined SharedPtr - reg_class(DepthStencilState); - reg_class(Fbo); - reg_class(GpuTimer); - reg_class(RasterizerState); - reg_class(Resource); - reg_class(ShaderResourceView); - reg_class(DepthStencilView); - reg_class(RenderTargetView); - reg_class(ConstantBufferView); - reg_class(UnorderedAccessView); - reg_class(Sampler); -// reg_class(StructuredBuffer); Doesn't work since it uses user-defined SharedPtr - reg_class(Texture); -// reg_class(TypedBuffer); Doesn't work since it uses user-defined SharedPtr - reg_class(Vao); - reg_class(VertexLayout); - - // Graphics - reg_class(Camera); - reg_class(Material); - reg_class(Model); - reg_class(Mesh); - reg_class(ObjectPath); - reg_class(Program); -// reg_class(GraphicsVars); Doesn't work since it uses user-defined SharedPtr -// reg_class(ComputeVars); Doesn't work since it uses user-defined SharedPtr - reg_class(GraphicsState); - reg_class(ComputeState); - reg_class(Light); - reg_class(LightProbe); - -#undef reg_class - } - - void ScriptBindings::registerScriptingObjects(pybind11::module& m) - { - globalEnums(m); - coreClasses(m); - samplerState(m); - toneMapping(m); - scene(m); - } -} \ No newline at end of file diff --git a/Framework/Source/Utils/TextRenderer.cpp b/Framework/Source/Utils/TextRenderer.cpp deleted file mode 100644 index 3d0bb4e43..000000000 --- a/Framework/Source/Utils/TextRenderer.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "Font.h" -#include "TextRenderer.h" -#include - -namespace Falcor -{ - static const glm::vec2 kVertexPos[] = - { - glm::vec2(0, 0), - glm::vec2(0, 1), - glm::vec2(1, 0), - - glm::vec2(1, 0), - glm::vec2(0, 1), - glm::vec2(1, 1), - }; - - TextRenderer::UniquePtr TextRenderer::create() - { - return UniquePtr(new TextRenderer()); - } - - Vao::SharedPtr createVAO(const Buffer::SharedPtr& pVB) - { - VertexLayout::SharedPtr pLayout = VertexLayout::create(); - VertexBufferLayout::SharedPtr pBufLayout = VertexBufferLayout::create(); - pBufLayout->addElement("POSITION", 0, ResourceFormat::RG32Float, 1, 0); - pBufLayout->addElement("TEXCOORD", 8, ResourceFormat::RG32Float, 1, 1); - pLayout->addBufferLayout(0, pBufLayout); - Vao::BufferVec buffers{ pVB }; - - return Vao::create(Vao::Topology::TriangleList, pLayout, buffers); - } - - TextRenderer::TextRenderer() - { - static const std::string kShaderFile("Framework/Shaders/TextRenderer.slang"); - - // Create a vertex buffer - const uint32_t vbSize = (uint32_t)(sizeof(Vertex)*kMaxBatchSize*arraysize(kVertexPos)); - mpVertexBuffer = Buffer::create(vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr); - - // Create the RenderState - mpPipelineState = GraphicsState::create(); - GraphicsProgram::SharedPtr pProgram = GraphicsProgram::createFromFile(kShaderFile, "vs", "ps"); - mpPipelineState->setProgram(pProgram); - mpPipelineState->setVao(createVAO(mpVertexBuffer)); - - // create the depth-state - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(false).setStencilTest(false); - mpPipelineState->setDepthStencilState(DepthStencilState::create(dsDesc)); - - // Rasterizer state - RasterizerState::Desc rsState; - rsState.setCullMode(RasterizerState::CullMode::None); - mpPipelineState->setRasterizerState(RasterizerState::create(rsState)); - - // Blend state - BlendState::Desc blendDesc; - blendDesc.setRtBlend(0, true).setRtParams(0, BlendState::BlendOp::Add, - BlendState::BlendOp::Add, - BlendState::BlendFunc::SrcAlpha, - BlendState::BlendFunc::OneMinusSrcAlpha, - BlendState::BlendFunc::One, - BlendState::BlendFunc::One); - - mpPipelineState->setBlendState(BlendState::create(blendDesc)); - mpFont = Font::create(); - - // Create and initialize the program variables - mpProgramVars = GraphicsVars::create(pProgram->getReflector(), true); - // Initialize the buffer - auto pCB = mpProgramVars["PerFrameCB"]; - mVarOffsets.vpTransform = pCB->getVariableOffset("gvpTransform"); - mVarOffsets.fontColor = pCB->getVariableOffset("gFontColor"); - mpProgramVars->setTexture("gFontTex", mpFont->getTexture()); - } - - TextRenderer::~TextRenderer() = default; - - void TextRenderer::begin(RenderContext* pRenderContext, const glm::vec2& startPos) - { - mCurPos = startPos; - mStartPos = startPos; - - const GraphicsState* pState = pRenderContext->getGraphicsState().get(); - - // Set the current FBO into the render state - mpPipelineState->setFbo(pState->getFbo()); - pRenderContext->pushGraphicsState(mpPipelineState); - - GraphicsState::Viewport VP(0, 0, (float)pState->getFbo()->getWidth(), (float)pState->getFbo()->getHeight(), 0, 1); - mpPipelineState->setViewport(0, VP); - - // Set the matrix - glm::mat4 vpTransform; - vpTransform[0][0] = 2 / VP.width; - vpTransform[1][1] = -2 / VP.height; - vpTransform[3][0] = -(VP.originX + VP.width) / VP.width; - vpTransform[3][1] = (VP.originX + VP.height) / VP.height; -#ifdef FALCOR_VK - vpTransform[1][1] *= -1.0f; - vpTransform[3][1] *= -1.0f; -#endif - - // Update the program variables - mpProgramVars["PerFrameCB"]->setVariable(mVarOffsets.vpTransform, vpTransform); - mpProgramVars["PerFrameCB"]->setVariable(mVarOffsets.fontColor, mTextColor); - pRenderContext->setGraphicsVars(mpProgramVars); - - // Map the buffer - mpBufferData = (Vertex*)mpVertexBuffer->map(Buffer::MapType::WriteDiscard); - } - - void TextRenderer::end(RenderContext* pRenderContext) - { - flush(pRenderContext); - mpVertexBuffer->unmap(); - pRenderContext->popGraphicsState(); - } - - void TextRenderer::flush(RenderContext* pRenderContext) - { - if(mCurrentVertexID != 0) - { - mpVertexBuffer->unmap(); - pRenderContext->draw(mCurrentVertexID, 0); - mCurrentVertexID = 0; - mpVertexBuffer->map(Buffer::MapType::WriteDiscard); - } - } - - void TextRenderer::renderLine(RenderContext* pRenderContext, const std::string& line) - { - for(size_t CurChar = 0; CurChar < line.size() ; CurChar++) - { - // Make sure we enough space for the next char - if(mCurrentVertexID + arraysize(kVertexPos) > mpVertexBuffer->getSize()) - { - flush(pRenderContext); - } - - char c = line[CurChar]; - if(c == '\n') - { - mCurPos.y += mpFont->getFontHeight(); - mCurPos.x = mStartPos.x; - } - else if(c == '\t') - { - mCurPos.x += mpFont->getTabWidth(); - } - else if(c == ' ') - { - mCurPos.x += mpFont->getLettersSpacing(); - } - else - { - // Regular character - const Font::CharTexCrdDesc& desc = mpFont->getCharDesc(c); - for (uint32_t i = 0; i < arraysize(kVertexPos); i++, mCurrentVertexID++) - { - glm::vec2 posScale = kVertexPos[i]; - glm::vec2 pos = desc.size * posScale; - pos += mCurPos; - mpBufferData[mCurrentVertexID].screenPos = pos; - mpBufferData[mCurrentVertexID].texCoord = desc.topLeft + desc.size * kVertexPos[i]; - } - - mCurPos.x += mpFont->getLettersSpacing(); - } - } - mCurPos.y += mpFont->getFontHeight(); - mCurPos.x = mStartPos.x; - } -} diff --git a/Framework/Source/Utils/TextRenderer.h b/Framework/Source/Utils/TextRenderer.h deleted file mode 100644 index f6bac139c..000000000 --- a/Framework/Source/Utils/TextRenderer.h +++ /dev/null @@ -1,116 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include -#include "Font.h" -#include "API/VAO.h" -#include "API/Buffer.h" -#include "Graphics/GraphicsState.h" -#include "Graphics/Program/ProgramVars.h" -#include "API/RenderContext.h" - -namespace Falcor -{ - class RenderContext; - - /** Class that renders text into the screen. - This class batches messages before drawing them for more efficient rendering. In order to do that, you have to enclose TextRenderer#renderLine() calls between TextRenderer#begin() and TextRenderer#end() calls. - */ - class TextRenderer - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - ~TextRenderer(); - - /** create a new object - */ - static UniquePtr create(); - - /** Start batching messages - \param[in] pRenderContext The rendering context - \param[in] startPos The screen-space position, from the top-left, of the first letter to draw - */ - void begin(RenderContext* pRenderContext, const glm::vec2& startPos); - - /** End batching. This will cause the render queue to flush and display the message to the screen. - */ - void end(RenderContext* pRenderContext); - - /** Render a line. After the function is called, an implicit newline is inserted into the message. - \param[in] line The line to draw. It can include newlines, tabs, carriage returns and regular ASCII characters. - */ - void renderLine(RenderContext* pRenderContext, const std::string& line); - - /** Returns the color of the text being rendered - \return current color The text color - */ - const glm::vec3& getTextColor() const { return mTextColor; } - - /** Set the color of the text being rendered - \param[in] color The text color - */ - void setTextColor(const glm::vec3& color) { mTextColor = color; } - - private: - TextRenderer(); - - struct Vertex - { - glm::vec2 screenPos; - glm::vec2 texCoord; - }; - - glm::vec2 mCurPos = {0, 0}; - glm::vec2 mStartPos = {0, 0}; - glm::vec3 mTextColor = glm::vec3(1, 1, 1); - - Font::UniquePtr mpFont; - Buffer::SharedPtr mpVertexBuffer; - - GraphicsState::SharedPtr mpPipelineState; - GraphicsVars::SharedPtr mpProgramVars; - - uint32_t mCurrentVertexID = 0; - - void createVertexBuffer(); - static const auto kMaxBatchSize = 1000; - - void flush(RenderContext* pRenderContext); - Vertex* mpBufferData = nullptr; - - struct - { - size_t vpTransform; - size_t fontColor; - } mVarOffsets; - }; -} \ No newline at end of file diff --git a/Framework/Source/Utils/Video/VideoDecoder.cpp b/Framework/Source/Utils/Video/VideoDecoder.cpp deleted file mode 100644 index b8ee31f6a..000000000 --- a/Framework/Source/Utils/Video/VideoDecoder.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#if 0 -#include "Framework.h" -#include "VideoDecoder.h" -#include "Utils/Platform/OS.h" -#include "Utils/BinaryFileStream.h" -extern "C" -{ -#include "libavcodec/avcodec.h" -#include "libavformat/avformat.h" -#include "libswscale/swscale.h" -} - -#include - -namespace Falcor -{ - float VideoDecoder::rationalToFloat(const AVRational& r) - { - return ((float)r.num / (float)r.den); - } - - VideoDecoder::UniquePtr VideoDecoder::create(const std::string& filename, uint32_t bufferedFrames, bool async) - { - auto pVideo = UniquePtr(new VideoDecoder()); - if(pVideo->load(filename, bufferedFrames, async) == false) - { - pVideo = nullptr; - } - - return pVideo; - } - - VideoDecoder::VideoDecoder() - { - } - - bool VideoDecoder::load(const std::string& filename, uint32_t bufferedFrames, bool async) - { - mFilename = filename; - mVidBufferCount = bufferedFrames; - - if(async) - { - mAsyncDecoding = std::make_shared>(std::async(std::launch::async, &VideoDecoder::bufferFrames, this)); - } - else - bufferFrames(); - - return true; - } - - VideoDecoder::~VideoDecoder() - { - // Flush the async operation - if(mAsyncDecoding) - { - mAsyncDecoding->get(); - } - - av_free(mpFrame); - - // Close the codec - avcodec_close(mpCodecCtx); - // Close the video file - avformat_close_input(&mpFormatCtx); - - for(auto& tex : (*mFrameTextures)) - if(tex) tex->evict(nullptr); - } - - void FlipRGBFrame(AVFrame* pFrame, int H) - { - uint8_t* templine = new uint8_t[pFrame->linesize[0]]; - - int hby2 = H / 2; - - for(int line = 0; line < hby2; line++) - { - auto* line1 = pFrame->data[0] + (line)* pFrame->linesize[0]; - auto* line2 = pFrame->data[0] + (H - 1 - line) * pFrame->linesize[0]; - - memcpy(templine, line1, pFrame->linesize[0]); - memcpy(line1, line2, pFrame->linesize[0]); - memcpy(line2, templine, pFrame->linesize[0]); - } - - delete[] templine; - } - - void VideoDecoder::bufferFrames() - { - if(mAsyncDecoding) - { - setThreadPriority(getCurrentThread(), ThreadPriorityType::Low); - setThreadAffinity(getCurrentThread(), (1<<5)|(1<<6)|(1<<7)|(1<<8)); - } - - if(mpFrame) - { - av_free(mpFrame); - mpFrame = nullptr; - } - - // Close the codec - if(mpCodecCtx) - { - avcodec_close(mpCodecCtx); - mpCodecCtx = nullptr; - } - // Close the video file - if(mpFormatCtx) - { - avformat_close_input(&mpFormatCtx); - mpFormatCtx = nullptr; - } - - // Register the codecs - avcodec_register_all(); - av_register_all(); - - if(avformat_open_input(&mpFormatCtx, mFilename.c_str(), NULL, NULL) != 0) - { - printf("Cannot open file\n"); - return; - } - - if(avformat_find_stream_info(mpFormatCtx, NULL) < 0) - { - printf("Couldn't find stream information.\n"); - return; - } - - for(uint32_t i = 0; i < mpFormatCtx->nb_streams; i++) - { - auto& stream = mpFormatCtx->streams[i]; - if(stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) - { - mVideoStream = i; - break; - } - } - - if(mVideoStream == -1) - { - return; - } - - auto& stream = mpFormatCtx->streams[mVideoStream]; - - // Get a pointer to the codec context for the video stream - mpCodecCtxOrig = stream->codec; - - mFPS = rationalToFloat(stream->avg_frame_rate); - - // Find the decoder for the video stream - mpCodec = avcodec_find_decoder(mpCodecCtxOrig->codec_id); - - if(mpCodec == NULL) - { - printf("Unsupported codec!\n"); - return; // Codec not found - } - - // Copy context - mpCodecCtx = avcodec_alloc_context3(mpCodec); - if(avcodec_copy_context(mpCodecCtx, mpCodecCtxOrig) != 0) - { - printf("Couldn't copy codec context\n"); - return; // Error copying codec context - } - - // Open codec - if(avcodec_open2(mpCodecCtx, mpCodec, nullptr) < 0) - { - return; // Could not open codec - } - - // Allocate video frame - mpFrame = av_frame_alloc(); - - - mFrames.clear(); - mFrames.reserve(mVidBufferCount); - - const bool async = mAsyncDecoding != nullptr; - - //printf("Buffering frames...\n"); - uint32_t frameIdx = 0; - - SwsContext *sws_ctx = sws_getContext( - mpCodecCtx->width, mpCodecCtx->height, mpCodecCtx->pix_fmt, - mpCodecCtx->width, mpCodecCtx->height, PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL); - - AVPacket packet; - while(av_read_frame(mpFormatCtx, &packet) >= 0 && (frameIdx < mVidBufferCount)) - { - if(packet.stream_index == mVideoStream) - { - int32_t isFrameDone; - avcodec_decode_video2(mpCodecCtx, mpFrame, &isFrameDone, &packet); - if(isFrameDone) - { - // Create either a single frame, or all frames one-by-one on CPU - if(async || mFrames.empty()) - { - // Inplace construction to avoid releasing of arrays - mFrames.push_back(Frame()); - new(&mFrames.back()) Frame(mpCodecCtx); - } - Frame& frame = mFrames.back(); - - // Convert the image from its native format to RGB - sws_scale(sws_ctx, (uint8_t const * const *)mpFrame->data, mpFrame->linesize, 0, mpCodecCtx->height, frame.mpFrameRGB->data, frame.mpFrameRGB->linesize); - FlipRGBFrame(frame.mpFrameRGB, mpCodecCtx->height); - if(!async) - uploadToGPU(frameIdx); - - frameIdx++; - } - } - av_free_packet(&packet); - } - //printf("Done reading %d frames\n", frameIdx); - - mRealFrameCount = frameIdx; - } - - void VideoDecoder::uploadToGPU(int frameStart) - { - if(!mFrameTextures) - mFrameTextures = std::make_shared(); - for(size_t i=0;i= mFrameTextures->size()) - { - mFrameTextures->push_back(Texture::create2D(mpCodecCtx->width, mpCodecCtx->height, ResourceFormat::RGBA8UnormSrgb, 1, 1, frame.mpFrameRGB->data[0])); - mFrameTextures->back()->makeResident(nullptr); - } - else - { - (*mFrameTextures)[frameStart + i]->uploadSubresourceData(frame.mpFrameRGB->data[0], frame.mFrameSize); - } - } - mFrames.clear(); - } - - Texture::SharedPtr VideoDecoder::getTextureForNextFrame(float curTime) - { - // Flush the async operation, upload everything to video memory - if(mAsyncDecoding) - { - mAsyncDecoding->get(); - mAsyncDecoding.reset(); - uploadToGPU(); - } - int curFrame = ((int)floor(curTime * mFPS)) % mRealFrameCount; - //SetBufferToTexture(m_VidBuffers[curFrame]); - //printf("time, frame: %f, %d\n", curTime, curFrame); - return (*mFrameTextures)[curFrame]; - } - - float VideoDecoder::getDuration() - { - // Flush the async operation, upload everything to video memory - if(mAsyncDecoding) - { - mAsyncDecoding->get(); - mAsyncDecoding.reset(); - uploadToGPU(); - } - - return ((float)mRealFrameCount) / mFPS; - } - - VideoDecoder::TexturePoolPtr VideoDecoder::getTexturePool() - { - return mFrameTextures; - } - - void VideoDecoder::setTexturePool(TexturePoolPtr& texturePool) - { - mFrameTextures = texturePool; - } - - VideoDecoder::Frame::Frame(AVCodecContext* codec) - { - mFrameSize = avpicture_get_size(PIX_FMT_RGBA, codec->width, codec->height); - - // Allocate an AVFrame structure - mpFrameRGB = av_frame_alloc(); - if(mpFrameRGB == NULL) - { - printf("Cannot allocate frame. Possibly out of CPU memory!\n"); - return; - } - mCpuVidBuffer = (uint8_t*)av_malloc(mFrameSize*sizeof(uint8_t)); - - avpicture_fill((AVPicture *)mpFrameRGB, mCpuVidBuffer, PIX_FMT_RGBA, codec->width, codec->height); - //printf("frame size is %d Mbytes\n", mFrameSize / (1024 * 1024)); - } - - VideoDecoder::Frame::~Frame() - { - if(mCpuVidBuffer) - av_free(mCpuVidBuffer); - if(mpFrameRGB) - av_free(mpFrameRGB); - } -} -#endif \ No newline at end of file diff --git a/Framework/Source/Utils/Video/VideoDecoder.h b/Framework/Source/Utils/Video/VideoDecoder.h deleted file mode 100644 index 9424a3686..000000000 --- a/Framework/Source/Utils/Video/VideoDecoder.h +++ /dev/null @@ -1,133 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include -#include -#include "API/Texture.h" - -struct AVFormatContext; -struct AVStream; -struct AVFrame; -struct AVPicture; -struct SwsContext; -struct AVCodecContext; -struct AVCodec; -struct AVRational; - -namespace Falcor -{ - /** Simple video decoder for high-framerate and high-resolution - playback of rendered videos. Currently decodes the first N frames - as textures before playing. - */ - class VideoDecoder - { - public: - using UniquePtr = std::unique_ptr; - using UniqueConstPtr = std::unique_ptr; - - typedef std::vector TexturePool; - typedef std::shared_ptr TexturePoolPtr; - - /** Create a new VideoDecoder object - \param[in] filename Input video file (with path) - \param[in] bufferFrames The maximum number of input frames to buffer as Texture objects. Default is 300. - \param[in] async Whether load operation should happen asynchronously - */ - static UniquePtr create(const std::string& filename, uint32_t bufferedFrames = 300, bool async = false); - ~VideoDecoder(); - - /** Get a texture object for the frame at current time - \param[in] curTime Time for which frame is sought - \return Texture pointer to texture object - */ - Texture::SharedPtr getTextureForNextFrame(float curTime); - - /** Return duration of video loaded (in seconds). - This uses the actual number of frames. Only makes sense - for videos shorter than the requested frame count. - \return Duration of video loaded in seconds. - */ - float getDuration(); - - /** Returns reusable shared texture pool - */ - TexturePoolPtr getTexturePool(); - - /** Sets reusable shared texture pool - */ - void setTexturePool(TexturePoolPtr& texturePool); - - /** Opens a file - \param[in] filename Input video file (with path) - \param[in] bufferFrames The maximum number of input frames to buffer as Texture objects. Default is 300. - \param[in] async Whether load operation should happen asynchronously - */ - bool load(const std::string& filename, uint32_t bufferedFrames, bool async); - - private: - /** Holds a single video frame on CPU - */ - struct Frame - { - Frame() {} - Frame(AVCodecContext* codec); - ~Frame(); - AVFrame* mpFrameRGB = nullptr; - uint8_t* mCpuVidBuffer = nullptr; - int mFrameSize = 0; - }; - - VideoDecoder(); - - void uploadToGPU(int frameStart = 0); - - std::string mFilename; - - AVFormatContext* mpFormatCtx = nullptr; - AVCodecContext* mpCodecCtxOrig = nullptr; - AVCodecContext* mpCodecCtx = nullptr; - AVCodec* mpCodec = nullptr; - AVFrame* mpFrame = nullptr; - std::vector mFrames; - - float mFPS = 30; - bool mFlipY = true; - unsigned mVideoStream = -1; - uint32_t mVidBufferCount = 300; - uint32_t mRealFrameCount = 0; - - std::shared_ptr> mAsyncDecoding = nullptr; - - TexturePoolPtr mFrameTextures; - - // helper routines - void bufferFrames(); - float rationalToFloat(const AVRational& r); - }; -} \ No newline at end of file diff --git a/Framework/Source/Utils/Video/VideoEncoderUI.cpp b/Framework/Source/Utils/Video/VideoEncoderUI.cpp deleted file mode 100644 index 15f035f58..000000000 --- a/Framework/Source/Utils/Video/VideoEncoderUI.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "VideoEncoderUI.h" -#include "Utils/Platform/OS.h" -#include "Utils/Gui.h" - -namespace Falcor -{ - static const Gui::DropdownList kCodecID = - { - { (uint32_t)VideoEncoder::CodecID::RawVideo, std::string("Uncompressed") }, - { (uint32_t)VideoEncoder::CodecID::H264, std::string("H.264") }, - { (uint32_t)VideoEncoder::CodecID::HEVC, std::string("HEVC(H.265)") }, - { (uint32_t)VideoEncoder::CodecID::MPEG2, std::string("MPEG2") }, - { (uint32_t)VideoEncoder::CodecID::MPEG4, std::string("MPEG4") } - }; - - VideoEncoderUI::UniquePtr VideoEncoderUI::create(uint32_t topLeftX, uint32_t topLeftY, uint32_t width, uint32_t height, Callback startCaptureCB, Callback endCaptureCB) - { - return UniquePtr(new VideoEncoderUI(topLeftX, topLeftY, width, height, startCaptureCB, endCaptureCB)); - } - - VideoEncoderUI::VideoEncoderUI(uint32_t topLeftX, uint32_t topLeftY, uint32_t width, uint32_t height, Callback startCaptureCB, Callback endCaptureCB) : mStartCB(startCaptureCB), mEndCB(endCaptureCB) - { - mWindowDims.x = topLeftX; - mWindowDims.y = topLeftY; - mWindowDims.width = width; - mWindowDims.height = height; - } - - void VideoEncoderUI::render(Gui* pGui) - { - if (mCapturing) - { - endCaptureUI(pGui); - } - else - { - startCaptureUI(pGui); - } - } - - void VideoEncoderUI::setCaptureState(bool state) - { - mCapturing = state; - } - - void VideoEncoderUI::startCaptureUI(Gui* pGui) - { - pGui->pushWindow("Video Capture", mWindowDims.width, mWindowDims.height, mWindowDims.x, mWindowDims.y); - pGui->addDropdown("Codec", kCodecID, (uint32_t&)mCodec); - pGui->addIntVar("Video FPS", (int32_t&)mFPS, 0, 240, 1); - - if(pGui->beginGroup("Codec Options")) - { - pGui->addFloatVar("Bitrate (Mbps)", mBitrate, 0, FLT_MAX, 0.01f); - pGui->addIntVar("GOP Size", (int32_t&)mGopSize, 0, 100000, 1); - pGui->endGroup(); - } - - pGui->addCheckBox("Capture UI", mCaptureUI); - pGui->addTooltip("Check this box if you want the GUI recorded"); - pGui->addCheckBox("Reset rendering", mResetOnFirstFrame); - pGui->addTooltip("Check this box if you want the rendering to be reset for the first frame, for example to reset temporal accumulation"); - pGui->addCheckBox("Use Time-Range", mUseTimeRange); - if(mUseTimeRange) - { - if (pGui->beginGroup("Time Range")) - { - pGui->addFloatVar("Start Time", mStartTime, 0, FLT_MAX, 0.001f); - pGui->addFloatVar("End Time", mEndTime, 0, FLT_MAX, 0.001f); - pGui->endGroup(); - } - } - - if (pGui->addButton("Start Recording")) - { - startCapture(); - } - if (pGui->addButton("Cancel", true)) - { - mEndCB(); - } - pGui->popWindow(); - } - - VideoEncoderUI::~VideoEncoderUI() = default; - - void VideoEncoderUI::startCapture() - { - if(saveFileDialog(VideoEncoder::getSupportedContainerForCodec(mCodec), mFilename)) - { - mCapturing = true; - - // Call the users callback - mStartCB(); - } - } - - void VideoEncoderUI::endCaptureUI(Gui* pGui) - { - pGui->pushWindow("Video Capture"); - if (pGui->addButton("End Recording")) - { - mEndCB(); - mCapturing = false; - } - pGui->popWindow(); - } -} \ No newline at end of file diff --git a/Framework/Source/VR/OpenVR/VRController.cpp b/Framework/Source/VR/OpenVR/VRController.cpp deleted file mode 100644 index d898d9816..000000000 --- a/Framework/Source/VR/OpenVR/VRController.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "FalcorConfig.h" - -#include "VRSystem.h" -#include "openvr.h" -#include "VRController.h" - -using namespace Falcor; - -// Private namespace -namespace -{ - // Matrix to convert OpenVR matrix to OpenGL matrix. - glm::mat4 convertOpenVRMatrix34(vr::HmdMatrix34_t mat) - { - return glm::mat4( mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, - mat.m[0][1], mat.m[1][1], mat.m[2][1], 0.0f, - mat.m[0][2], mat.m[1][2], mat.m[2][2], 0.0f, - mat.m[0][3], mat.m[1][3], mat.m[2][3], 1.0f ); - } -} - -VRController::SharedPtr VRController::create( vr::IVRSystem *vrSys, vr::IVRRenderModels *modelClass ) -{ - SharedPtr ctrl = SharedPtr( new VRController ); - - // Setup default controller (inactive) - ctrl->mChangedButtons = ctrl->mPressedButtons = ctrl->mTouchedButtons = 0ull; - ctrl->mTriggerSqueeze = 0.0f; - ctrl->mIsActive = ctrl->mIsTracking = false; - ctrl->mTrackpadPosition = glm::vec2(0.0f); - ctrl->mControllerCenter = ctrl->mControllerVector = ctrl->mModelSpaceAimPoint = ctrl->mCurrentAimPoint = glm::vec3(0.0f); - ctrl->mWorldMatrix = glm::mat4(); - - // Setup internal OpenVR state - ctrl->mpVrSys = vrSys; - ctrl->mpRenderModels = modelClass; - ctrl->mOpenVRDeviceID = -1; // not yet known. - - return ctrl; -} - -bool VRController::hasButtonStateChanged( Button queryButton ) -{ - // Check if the button changed, then turn it off - bool hasChanged = ((mChangedButtons & uint64_t(queryButton)) != 0ull); - mChangedButtons &= ~uint64_t(queryButton); - return hasChanged; -} - -bool VRController::isButtonJustDown( Button queryButton ) -{ - // Explicity pull these apart to guarantee we don't destructively check button change - // state when not necessary. - if ( !isButtonDown( queryButton ) ) return false; - return hasButtonStateChanged( queryButton ); -} - -bool VRController::isButtonJustTouched( Button queryButton ) -{ - // Explicity pull these apart to guarantee we don't destructively check button change - // state when not necessary. - if ( !isButtonTouched( queryButton ) ) return false; - return hasButtonStateChanged( queryButton ); -} - -void VRController::setControllerAimPoint( glm::vec3 &atPoint ) -{ - mModelSpaceAimPoint = atPoint; -} - -glm::vec3 VRController::getControllerAimPoint( void ) const -{ - return mCurrentAimPoint; -} - -void VRController::updateOnButtonEvent( uint64_t button, ButtonEvent eventType ) -{ - // If no event, return now. - if (eventType == ButtonEvent::None) return; - - // A change has occurred, so note that. - mChangedButtons |= uint64_t(button); - - // Are we updating pressed or touched buttons? - uint64_t *changeBits = ((eventType == ButtonEvent::Press) || (eventType == ButtonEvent::Unpress)) - ? &mPressedButtons - : &mTouchedButtons; - - // Go and update the button state bits - if ( ( eventType == ButtonEvent::Press ) || ( eventType == ButtonEvent::Touch ) ) - { - *changeBits |= uint64_t(button); // Add the button to the pressed/touched list - } - else - { - *changeBits &= ~uint64_t(button); // Remove the button from the pressed/touch list - } -} - -void VRController::updateOnNewPose( vr::TrackedDevicePose_t *newPose, int32_t deviceID ) -{ - if (!mpVrSys || !newPose) return; - - mIsActive = newPose->bDeviceIsConnected; - mIsTracking = newPose->bPoseIsValid; - mOpenVRDeviceID = deviceID; // Sadly OpenVR doesn't stick to a consistent ID for things. *sigh* - - // Nothing is really valid beyond this point... - if (!newPose->bPoseIsValid) return; - - // Grab the current controller state. Ideally, we'd pass this in, but due to templated class forwarding - // issues, this is remarkably hard. So, move it in here. - vr::VRControllerState_t curState; - mpVrSys->GetControllerState( mOpenVRDeviceID, &curState, sizeof(curState)); - - // Now that we have our current pose & state, update internal variables - mWorldMatrix = convertOpenVRMatrix34( newPose->mDeviceToAbsoluteTracking ); - mTriggerSqueeze = curState.rAxis[1].x; - mTrackpadPosition = glm::vec2(curState.rAxis[0].x, curState.rAxis[0].y); - mControllerCenter = glm::vec3( mWorldMatrix * glm::vec4(0,0,0,1) ); - mControllerVector = glm::normalize( glm::vec3( mWorldMatrix * glm::vec4(0,0,-1,1) ) - mControllerCenter ); - mCurrentAimPoint = glm::vec3( mWorldMatrix * glm::vec4( mModelSpaceAimPoint, 1 ) ); -} - -void VRController::updateOnActivate( int32_t deviceID ) -{ - // Remember which device ID we are (though this is transient; OpenVR changes it as new devices are added) - mOpenVRDeviceID = deviceID; - mIsActive = true; - - // See if this device has a renderable model - uint32_t unRequiredBufferLen = mpVrSys->GetStringTrackedDeviceProperty( mOpenVRDeviceID, vr::Prop_RenderModelName_String, NULL, 0, NULL ); - if ( unRequiredBufferLen != 0 ) - { - // If so, get a string describing it - char *pchBuffer = new char[unRequiredBufferLen]; - unRequiredBufferLen = mpVrSys->GetStringTrackedDeviceProperty( mOpenVRDeviceID, vr::Prop_RenderModelName_String, pchBuffer, unRequiredBufferLen, NULL ); - mModelName = pchBuffer; - delete[] pchBuffer; - } - -} - -Model::SharedPtr VRController::getRenderableModel( Texture::SharedPtr overrideTexture ) -{ - // If we already got a model, it won't change. so go ahead and return the same one. - if ( mpRenderableModel ) - { - return mpRenderableModel; - } - - // Go ahead and pull our model from the OpenVR system - vr::RenderModel_t* pModel = nullptr; - - vr::EVRRenderModelError modelErr; - do - { - modelErr = mpRenderModels->LoadRenderModel_Async(mModelName.c_str(), &pModel); - } - while(modelErr == vr::VRRenderModelError_Loading); - - if (modelErr != vr::VRRenderModelError_None || !pModel) - return nullptr; - - // create and populate the texture - mpModelTexture = overrideTexture; - vr::RenderModel_TextureMap_t *pTexture = nullptr; - - vr::EVRRenderModelError texErr; - do - { - texErr = mpRenderModels->LoadTexture_Async(pModel->diffuseTextureId, &pTexture); - }while(texErr == vr::VRRenderModelError_Loading); - - if (texErr != vr::VRRenderModelError_None || !pTexture) - return nullptr; - - if ( !overrideTexture ) - mpModelTexture = Texture::create2D( pTexture->unWidth, pTexture->unHeight, - ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, - (const void *) pTexture->rubTextureMapData ); - - // OpenVR models use uint16_t index buffers. Convert to uint32_t for Falcor. - uint32_t *newIdxBuf = (uint32_t *) malloc( pModel->unTriangleCount * 3 * sizeof( uint32_t ) ); - for ( uint32_t i = 0; i < pModel->unTriangleCount * 3; i++ ) - newIdxBuf[i] = pModel->rIndexData[i]; - - // Use the SimpleModelImporter to create a Falcor model from memory. - SimpleModelImporter::VertexFormat vertLayout; - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::Position, 3, AttribFormat::AttribFormat_F32 } ); - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::Normal, 3, AttribFormat::AttribFormat_F32 } ); - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::TexCoord, 2, AttribFormat::AttribFormat_F32 } ); - Model::SharedPtr ctrlModel = SimpleModelImporter::create( vertLayout, - sizeof( vr::RenderModel_Vertex_t ) * pModel->unVertexCount, - pModel->rVertexData, - pModel->unTriangleCount * 3 * sizeof( uint32_t ), - newIdxBuf, - mpModelTexture ); - - // Free our temporary memory - free( newIdxBuf ); - - return ctrlModel; -} - -Model::SharedPtr VRController::getRenderableAxes( void ) -{ - float controllerWandLength = 0.75f; - - // If we already got a model, it won't change. so go ahead and return the same one. - if ( mpRenderableAxes ) - { - return mpRenderableAxes; - } - - - // point (relative to controler), color, point, color, point, ... - float axisData[] = { 0.1f, 0, 0, 1, 0, 0, -0.1f, 0, 0, 1, 0, 0, /* red = left/right */ - 0, 0.1f, 0, 0, 1, 0, 0, -0.1f, 0, 0, 1, 0, /* green = forward/back */ - 0, 0, 0.01f, 0, 0, 1, 0, 0, -controllerWandLength, 0, 0, 1 }; /* blue = up/down */ - uint32_t idxData[] = { 0, 1, 2, 3, 4, 5 }; - - // Use the SimpleModelImporter to create a Falcor model from memory. - SimpleModelImporter::VertexFormat vertLayout; - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::Position, 3, AttribFormat::AttribFormat_F32 } ); - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::Color, 3, AttribFormat::AttribFormat_F32 } ); - mpRenderableAxes = SimpleModelImporter::create( vertLayout, - sizeof( axisData ), - axisData, - sizeof( idxData ), - idxData, - nullptr, - Vao::Topology::LineList ); - - return mpRenderableAxes; -} diff --git a/Framework/Source/VR/OpenVR/VRController.h b/Framework/Source/VR/OpenVR/VRController.h deleted file mode 100644 index 24a0d405c..000000000 --- a/Framework/Source/VR/OpenVR/VRController.h +++ /dev/null @@ -1,209 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -// This is wrapper class around OpenVR controllers. It allows you to determine -// the state of the contoller (button presses), the current position of the -// controler, information about its orientation, and whether it is currently -// tracked correctly. -// -// Chris Wyman (12/15/2015) - -#pragma once -#include "Framework.h" -#include "glm/mat4x4.hpp" -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "API/FBO.h" -#include "API/RenderContext.h" -#include "Graphics/Program/Program.h" -#include "API/Texture.h" - -// Forward declare OpenVR system class types to remove "openvr.h" dependencies from Falcor headers -namespace vr { - class IVRSystem; // A generic system with basic API to talk with a VR system - class IVRCompositor; // A class that composite rendered results with appropriate distortion into the HMD - class IVRRenderModels; // A class giving access to renderable geometry for things like controllers - class IVRChaperone; - class IVRRenderModels; - struct TrackedDevicePose_t; -} - -namespace Falcor -{ - // Forward declare the VRSystem class to avoid header cycles. - class VRSystem; - - /** High-level OpenVR controller abstraction - */ - class VRController - { - public: - // Declare standard Falcor shared pointer types. - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - // Enums for all button bits exposed by OpenVR. Not all are accessible with Vive Controllers. - // Non-accessible bits are not currently exposed in the enum. - enum Button : uint64_t - { - None = 0, - RedButton = 1ull << 1, - Grip = 1ull << 2, - Touchpad = 1ull << 32, - Trigger = 1ull << 33, - }; - - // Enums for controller button events - enum class ButtonEvent - { - None, Press, Unpress, Touch, Untouch - }; - - // Controller constructor. Automatically called by other wrapper classes. - static SharedPtr create( vr::IVRSystem *vrSys, vr::IVRRenderModels *modelClass = 0 ); - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Simple accessor methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Check if the controller is active an/or tracking. - bool isControllerActive( void ) const { return mIsActive; } - bool isControllerTracking( void ) const { return mIsTracking; } - - // True if specified button is currently down or touched. - bool isButtonDown( Button queryButton ) const { return ( mPressedButtons & uint64_t(queryButton) ) != 0ull; } - bool isButtonTouched( Button queryButton ) const { return ( mTouchedButtons & uint64_t(queryButton) ) != 0ull; } - - // True if button state has changed just recently (since last call to isButtonJust*() or hasButtonStateChanged()) - bool isButtonJustDown( Button queryButton ); - bool isButtonJustTouched( Button queryButton ); - - // True if queried button state has changed since last calling hasButtonStateChanged() for specified button - bool hasButtonStateChanged( Button queryButton ); - - // Get a matrix transforming from model/object to world coordinates based on tracking data - glm::mat4 getToWorldMatrix( void ) const { return mWorldMatrix; } - - // Returns a [0..1] value representing how far the trigger is depressed. Values < 0.05 are unreliable. Values > 0.66 - // only occur with fairly hard grip. isButtonDown( Trigger ) essentially uses a threshold on this inside OpenVR. - float getTriggerDepression( void ) const { return mTriggerSqueeze; } - - // If trackpad is pressed (or touched) returns the position of this press in [-1..1] x [-1..1] - glm::vec2 getTrackpadPosition( void ) const { return mTrackpadPosition; } - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Controller positional accessors - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Get the center of the controller in world space - glm::vec3 getControllerCenter( void ) const { return mControllerCenter; } - - // Gets a world-space unit vector pointing out of the top of the Vive controller (think: like a sword blade) - glm::vec3 getControllerVector( void ) const { return mControllerVector; } - - // Get the world-space location of a user-specified "aim point" in the controllers' coordinate space - glm::vec3 getControllerAimPoint( void ) const; - - // Set the model-space "aim point." setControllerAimPoint( vec3(0,0,-1) ) is one unit "above" the - // controller (i.e., in the direction getControllerVector()) - void setControllerAimPoint( glm::vec3 &atPoint ); - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Controller geometric accessors - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Get a Falcor model representing the controller. All controllers will return the same geometry, - // though you *can* pass your own texture in if you'd like to override OpenVR's baked AO texture. - Model::SharedPtr getRenderableModel( Texture::SharedPtr overrideTexture = nullptr ); - - // When rendering, if you directly need the texture for the model, you can grab it here - Texture::SharedPtr getRenderableModelTexture( void ) { return mpModelTexture; } - - // Get a wiredframe set of axes to render instead of / under the controllers. - // -> TODO: Make more flexible instead of using Chris' preferred axis style - Model::SharedPtr getRenderableAxes( void ); - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Wrapper-specific methods; Shouldn't need to use outside of OpenVR wrapper code, generally. - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // When a button even occurs, call this to update state viewable by above accessors - void updateOnButtonEvent( uint64_t button, ButtonEvent eventType ); - - // When a new controller pose is provided by OpenVR, call this to update state viewable by above - // accessors. deviceID is OpenVR's ID for the objects it tracks. - void updateOnNewPose( vr::TrackedDevicePose_t *newPose, int32_t deviceID ); - - // If you want to manually toggle the mIsActive state, call this. The state is handled automatically - // by OpenVR pose information, but specific event updates can get passed here. - void updateActivatedState( bool currentlyActive ) { mIsActive = currentlyActive; } - - // When the controller first activates, there'a a bunch of state info about the controller - // OpenVR has that we don't -- the name of the model inside the DLL, and other controller - // properties. When the controller is first activated, calling this method allows us - // to know what we're dealing with. Without calling this, some more advanced properties - // of this class (i.e., getting the model geometry) won't work correctly. - void updateOnActivate( int32_t deviceID ); - - // If we need to query OpenVR about this controller, you need the internal OpenVR device ID - int32_t getDeviceID( void ) const { return mOpenVRDeviceID; } - - private: - uint64_t mChangedButtons; // Which buttons have recently changed state? - uint64_t mPressedButtons; // Which buttons are currently depressed? - uint64_t mTouchedButtons; // Which buttons are currently touched? - - float mTriggerSqueeze; // How much is the trigger squeezed - glm::vec2 mTrackpadPosition; // Where is the trackpad touched/pressed? - - bool mIsActive; // Is the controller active? - bool mIsTracking; // Is the controller correctly tracking? - - glm::vec3 mControllerCenter; // The center of the controller - glm::vec3 mControllerVector; // The pointing direction of the controller - - glm::mat4 mWorldMatrix; // The model-to-world matrix for the controller - - glm::vec3 mModelSpaceAimPoint; // The user-specified aim point (input via setControllerAimPoint() - glm::vec3 mCurrentAimPoint; // The aim point in the current controller reference frame - - Model::SharedPtr mpRenderableModel; // A Falcor renderable model. Can be NULL if OpenVR has no model for controller! - Model::SharedPtr mpRenderableAxes; - Texture::SharedPtr mpModelTexture; - - // Internals for accessing OpenVR state. Not user accessible - vr::IVRRenderModels *mpRenderModels; - vr::IVRSystem *mpVrSys; - int32_t mOpenVRDeviceID; - std::string mModelName; - - }; -} diff --git a/Framework/Source/VR/OpenVR/VRDisplay.cpp b/Framework/Source/VR/OpenVR/VRDisplay.cpp deleted file mode 100644 index bbca26c59..000000000 --- a/Framework/Source/VR/OpenVR/VRDisplay.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "FalcorConfig.h" - -#include "VRSystem.h" -#include "openvr.h" -#include "VRController.h" -#include "VRTrackerBox.h" -#include "VRDisplay.h" -#include "Graphics/Camera/Camera.h" - -namespace Falcor -{ - // Private namespace - namespace - { - // Matrix to convert OpenVR matrix to OpenGL matrix. - glm::mat4 convertOpenVRMatrix34(vr::HmdMatrix34_t mat) - { - return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, - mat.m[0][1], mat.m[1][1], mat.m[2][1], 0.0f, - mat.m[0][2], mat.m[1][2], mat.m[2][2], 0.0f, - mat.m[0][3], mat.m[1][3], mat.m[2][3], 1.0f); - } - - // Matrix to convert OpenVR matrix to OpenGL matrix. - glm::mat4 convertOpenVRMatrix44(vr::HmdMatrix44_t mat) - { -#ifdef FALCOR_VK - // Vulkan clip-space is +Y down - const float m11 = -mat.m[1][1]; -#else - const float m11 = mat.m[1][1]; -#endif - return glm::mat4( - mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], - mat.m[0][1], m11, mat.m[2][1], mat.m[3][1], - mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], - mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]); - } - } - - VRDisplay::SharedPtr VRDisplay::create(vr::IVRSystem *vrSys, vr::IVRRenderModels *modelClass) - { - SharedPtr ctrl = SharedPtr(new VRDisplay); - - // Setup default tracker (inactive) - ctrl->mIsTracking = false; - ctrl->mPosition = glm::vec3(0.0f); - ctrl->mWorldMat = glm::mat4(); - ctrl->mNearFarPlanes = glm::vec2(0.01f, 20.0f); - - // Setup internal OpenVR state - ctrl->mpVrSys = vrSys; - ctrl->mpRenderModels = modelClass; - ctrl->mDeviceID = vr::k_unTrackedDeviceIndex_Hmd; - - ctrl->mOffsetMats[(uint32_t)Eye::Left] = glm::inverse(convertOpenVRMatrix34(vrSys->GetEyeToHeadTransform(vr::Eye_Left))); - ctrl->mOffsetMats[(uint32_t)Eye::Right] = glm::inverse(convertOpenVRMatrix34(vrSys->GetEyeToHeadTransform(vr::Eye_Right))); - - // Grab guesses for projection matricies, so we have *something* in case the user asks for a matrix prior to specifying a near/far plane - // -> We're totally guessing at near/far planes here. User needs to set near/far planes for real on their own. - ctrl->mProjMats[(uint32_t)Eye::Left] = convertOpenVRMatrix44(vrSys->GetProjectionMatrix(vr::Eye_Left, ctrl->mNearFarPlanes.x, ctrl->mNearFarPlanes.y)); - ctrl->mProjMats[(uint32_t)Eye::Right] = convertOpenVRMatrix44(vrSys->GetProjectionMatrix(vr::Eye_Right, ctrl->mNearFarPlanes.x, ctrl->mNearFarPlanes.y)); - - // Get the recommended per-eye render size - uint32_t recSize[2]; - vrSys->GetRecommendedRenderTargetSize(&recSize[0], &recSize[1]); - ctrl->mRecRenderSz = glm::ivec2(recSize[0], recSize[1]); - ctrl->mAspectRatio = float(recSize[0]) / float(recSize[1]); - - float recTanFovY = ctrl->mProjMats[0][1][1]; - float tanFovY = 1 / recTanFovY; - float fovY = 2 * atan(tanFovY); - ctrl->mFovY = fovY; - - // // GetWindowBounds has been moved to IVRDisplayComponent - // - //// Get the native screen size (and the compositor offset, which should be (0,0) if rendering on the HMD in 'fullscreen' mode) - //int32_t screenPos[2]; - //uint32_t dispSize[2]; - //ctrl->GetWindowBounds( &screenPos[0], &screenPos[1], &dispSize[0], &dispSize[1] ); - //ctrl->mNativeDisplayRes = glm::ivec2( dispSize[0], dispSize[1] ); - //ctrl->mCompositorOffset = glm::ivec2( screenPos[0], screenPos[1] ); - - - // See if this device has a renderable model - uint32_t unRequiredBufferLen = ctrl->mpVrSys->GetStringTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_RenderModelName_String, NULL, 0, NULL); - if(unRequiredBufferLen != 0) - { - // If so, get a string describing it - char *pchBuffer = new char[unRequiredBufferLen]; - unRequiredBufferLen = ctrl->mpVrSys->GetStringTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_RenderModelName_String, pchBuffer, unRequiredBufferLen, NULL); - ctrl->mModelName = pchBuffer; - delete[] pchBuffer; - } - - - return ctrl; - } - - void VRDisplay::setDepthRange(float nearZ, float farZ) - { - mNearFarPlanes = glm::vec2(nearZ, farZ); - mProjMats[(uint32_t)Eye::Left] = convertOpenVRMatrix44(mpVrSys->GetProjectionMatrix(vr::Eye_Left, mNearFarPlanes.x, mNearFarPlanes.y)); - mProjMats[(uint32_t)Eye::Right] = convertOpenVRMatrix44(mpVrSys->GetProjectionMatrix(vr::Eye_Right, mNearFarPlanes.x, mNearFarPlanes.y)); - } - - void VRDisplay::updateOnNewPose(vr::TrackedDevicePose_t *newPose) - { - if(!newPose->bPoseIsValid) - { - mIsTracking = false; - return; - } - mIsTracking = true; - - mWorldMat = glm::inverse(convertOpenVRMatrix34(newPose->mDeviceToAbsoluteTracking)); - - // Since the matrix is inverted (i.e., GL camera is *truly* at (0,0,0) and we're moving the scene instead), - // we can't simply apply the matrix to (0,0,0) as with other matrices. We need to do that, then invert - // the translation. - glm::vec4 camTransform = mWorldMat * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - mPosition = glm::vec3(-camTransform.x, -camTransform.y, -camTransform.z); - - // In case the user needs a view matrix frequently, premultiply it here. - mViewMats[0] = mOffsetMats[0] * mWorldMat; - mViewMats[1] = mOffsetMats[1] * mWorldMat; - - } - - Model::SharedPtr VRDisplay::getRenderableModel(Texture::SharedPtr overrideTexture) - { - // If we already got a model, it won't change. so go ahead and return the same one. - if(mpRenderableModel) - { - return mpRenderableModel; - } - - // Go ahead and pull our model from the OpenVR system - vr::RenderModel_t* pModel = nullptr; - - vr::EVRRenderModelError modelErr; - do - { - modelErr = mpRenderModels->LoadRenderModel_Async(mModelName.c_str(), &pModel); - } while(modelErr == vr::VRRenderModelError_Loading); - - if(modelErr != vr::VRRenderModelError_None) - return nullptr; - - // create and populate the texture - mpModelTexture = overrideTexture; - vr::RenderModel_TextureMap_t *pTexture = nullptr; - - vr::EVRRenderModelError texErr; - do - { - texErr = mpRenderModels->LoadTexture_Async(pModel->diffuseTextureId, &pTexture); - } while(texErr == vr::VRRenderModelError_Loading); - - if(texErr != vr::VRRenderModelError_None) - return nullptr; - - if(!overrideTexture) - mpModelTexture = Texture::create2D(pTexture->unWidth, pTexture->unHeight, - ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, - (const void *)pTexture->rubTextureMapData); - - // OpenVR models use uint16_t index buffers. Convert to uint32_t for Falcor. - uint32_t *newIdxBuf = (uint32_t *)malloc(pModel->unTriangleCount * 3 * sizeof(uint32_t)); - for(uint32_t i = 0; i < pModel->unTriangleCount * 3; i++) - newIdxBuf[i] = pModel->rIndexData[i]; - - // Use the SimpleModelImporter to create a Falcor model from memory. - SimpleModelImporter::VertexFormat vertLayout; - vertLayout.attribs.push_back({SimpleModelImporter::AttribType::Position, 3, AttribFormat::AttribFormat_F32}); - vertLayout.attribs.push_back({SimpleModelImporter::AttribType::Normal, 3, AttribFormat::AttribFormat_F32}); - vertLayout.attribs.push_back({SimpleModelImporter::AttribType::TexCoord, 2, AttribFormat::AttribFormat_F32}); - Model::SharedPtr ctrlModel = SimpleModelImporter::create(vertLayout, - sizeof(vr::RenderModel_Vertex_t) * pModel->unVertexCount, - pModel->rVertexData, - pModel->unTriangleCount * 3 * sizeof(uint32_t), - newIdxBuf, - mpModelTexture); - - // Free our temporary memory - free(newIdxBuf); - - return ctrlModel; - } -} diff --git a/Framework/Source/VR/OpenVR/VRDisplay.h b/Framework/Source/VR/OpenVR/VRDisplay.h deleted file mode 100644 index 06aea6302..000000000 --- a/Framework/Source/VR/OpenVR/VRDisplay.h +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -// This is wrapper class around OpenVR displays (aka HMDs). This is a fairly -// important class, providing access to rendering view and project matrices -// (for both eyes), plus the recommended render target size. -// -// Chris Wyman (12/15/2015) - -#pragma once - -#include "Framework.h" -#include "glm/mat4x4.hpp" -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "API/FBO.h" -#include "API/RenderContext.h" -#include "Graphics/Program/Program.h" -#include "API/Texture.h" -#include "Graphics/Model/Model.h" - -// Forward declare OpenVR system class types to remove "openvr.h" dependencies from Falcor headers -namespace vr -{ - class IVRSystem; // A generic system with basic API to talk with a VR system - class IVRCompositor; // A class that composite rendered results with appropriate distortion into the HMD - class IVRRenderModels; // A class giving access to renderable geometry for things like controllers - class IVRChaperone; - class IVRRenderModels; - struct TrackedDevicePose_t; -} - -namespace Falcor -{ - // Forward declare the VRSystem class to avoid header cycles. - class VRSystem; - class Camera; - - /** High-level OpenVR display abstraction - */ - class VRDisplay - { - public: - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Types and enums - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Declare standard Falcor shared pointer types. - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - enum class Eye - { - Right = 0, - Left = 1, - }; - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Constructors & destructors - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Controller constructor. Automatically called by other wrapper classes. - static SharedPtr create( vr::IVRSystem *vrSys, vr::IVRRenderModels *modelClass = 0 ); - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Simple accessor methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Check if the HMD is correctly being tracked - bool isTracking( void ) const { return mIsTracking; } - - // Gets position of center of HMD in world-space - glm::vec3 getPosition( void ) const { return mPosition; } - - // Gets model-space to world-space transform for the HMD - glm::mat4 getWorldMatrix( void ) const { return mWorldMat; } - - // Gets the projection matrix for a specified eye - glm::mat4 getProjectionMatrix( VRDisplay::Eye whichEye ) const { return mProjMats[(uint32_t)whichEye]; } - - // Gets the offset matrix from center of HMD to specified eye. - glm::mat4 getOffsetMatrix(VRDisplay::Eye whichEye) const { return mOffsetMats[(uint32_t)whichEye]; } - - // Get's the view matrix for specified eye (equiv. to getOffsetMatrix(whichEye) * getToWorldMatrix()) - glm::mat4 getViewMatrix(VRDisplay::Eye whichEye) const { return mViewMats[(uint32_t)whichEye]; } - - // Gets the HMD's native resolution (i.e., the size it shows up in Windows as a "monitor") - glm::ivec2 getNativeResolution( void ) const { return mNativeDisplayRes; } - - // Returns the recommended size to render at (*independently* for each eye) - glm::ivec2 getRecommendedRenderSize( void ) const { return mRecRenderSz; } - - float getFovY() const { return mFovY; } - float getAspectRatio() const { return mAspectRatio; } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Mutator methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Set near & far values for the projection matrix. Without calling this, defaults to [0.01...20.0] - void setDepthRange( float nearZ, float farZ ); - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // HMD geometric accessors. (Note: HMD models are often pretty generic, since it's not usually - // useful to see them. This may get a model, it just may not be representative of the - // HMD that is actually being used...) - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Get a Falcor model representing the lighthouse tracker. All trackers will return the same geometry, - // though you *can* pass your own texture in if you'd like to override OpenVR's baked AO texture. - Model::SharedPtr getRenderableModel( Texture::SharedPtr overrideTexture = nullptr ); - - // When rendering, if you directly need the texture for the model, you can grab it here - Texture::SharedPtr getRenderableModelTexture( void ) { return mpModelTexture; } - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Wrapper-specific methods; Shouldn't need to use outside of OpenVR wrapper code, generally. - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // When the HMD is updated, the new pose should be passed in here to update all our positioning state - void updateOnNewPose( vr::TrackedDevicePose_t *newPose ); - - private: - bool mIsTracking; - glm::mat4 mOffsetMats[2]; - glm::mat4 mViewMats[2]; - glm::mat4 mProjMats[2]; - glm::mat4 mWorldMat; - glm::vec3 mPosition; - glm::ivec2 mRecRenderSz; - glm::ivec2 mNativeDisplayRes; - glm::ivec2 mCompositorOffset; - glm::vec2 mNearFarPlanes; - - int32_t mDeviceID; - vr::IVRRenderModels *mpRenderModels; - vr::IVRSystem *mpVrSys; - std::string mModelName; - - Texture::SharedPtr mpModelTexture; - Model::SharedPtr mpRenderableModel; - - float mAspectRatio; - float mFovY; - }; - -} // end namespace Falcor diff --git a/Framework/Source/VR/OpenVR/VRPlayArea.h b/Framework/Source/VR/OpenVR/VRPlayArea.h deleted file mode 100644 index a010248d9..000000000 --- a/Framework/Source/VR/OpenVR/VRPlayArea.h +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -// This is wrapper class around OpenVR play areas, and can provide information -// about the play area size (to appropriately bound user interactions), -// as well as determining id the play area is fully set up and calibrated -// (otherwise you can't really trust what it says). -// -// Chris Wyman (12/15/2015) - -#pragma once - -#include "Framework.h" -#include "glm/mat4x4.hpp" -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "API/FBO.h" -#include "API/RenderContext.h" -#include "Graphics/Program/Program.h" -#include "API/Texture.h" -#include "Graphics/Model/Model.h" - -// Forward declare OpenVR system class types to remove "openvr.h" dependencies from Falcor headers -namespace vr -{ - class IVRSystem; // A generic system with basic API to talk with a VR system - class IVRCompositor; // A class that composite rendered results with appropriate distortion into the HMD - class IVRRenderModels; // A class giving access to renderable geometry for things like controllers - class IVRChaperone; - class IVRRenderModels; - struct TrackedDevicePose_t; -} - -namespace Falcor -{ - // Forward declare the VRSystem class to avoid header cycles. - class VRSystem; - - /** High-level OpenVR controller abstraction - */ - class VRPlayArea - { - public: - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Types and enums - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Declare standard Falcor shared pointer types. - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Constructors & destructors - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Controller constructor. Automatically called by other wrapper classes. - static SharedPtr create( vr::IVRSystem *vrSys, vr::IVRChaperone *chaperone ); - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Simple accessor methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Returns if OpenVR thinks it is calibrated. Partially calibrated means there's enough info to - // run, but they not know where the play area is or may think they've moved since calibration - bool isCalibrated( void ) const; - bool isPartiallyCalibrated( void ) const; - - // Get's the area available for movement. Returns vec2(a,b). If no play area is available, a = b = 0. - // Otherwise the play area's x-bounds of the scene go from [-a..a], the z-bounds go from [-b..b], and - // the y-bounds go from [0..room-height]. I don't see any definition of the height anywhere, but it - // looks to be about 2.5 meters, despite the actual room height. - glm::vec2 getSize( void ) const; - - private: - vr::IVRSystem *mpVrSys; - vr::IVRChaperone *mpChaperone; - }; - -} // end namespace Falcor diff --git a/Framework/Source/VR/OpenVR/VRSystem.cpp b/Framework/Source/VR/OpenVR/VRSystem.cpp deleted file mode 100644 index 2407fdc0b..000000000 --- a/Framework/Source/VR/OpenVR/VRSystem.cpp +++ /dev/null @@ -1,531 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "FalcorConfig.h" - -#include "VRSystem.h" -#include "openvr.h" -#include "API/Device.h" -#include "Utils/StringUtils.h" - -namespace Falcor -{ -#ifdef FALCOR_D3D12 - static vr::D3D12TextureData_t prepareSubmitData(const Texture::SharedConstPtr& pTex, RenderContext* pRenderCtx) - { - vr::D3D12TextureData_t submitTex; - submitTex.m_pResource = pTex->getApiHandle(); - submitTex.m_pCommandQueue = pRenderCtx->getLowLevelData()->getCommandQueue(); - submitTex.m_nNodeMask = 0; - return submitTex; - } - - static vr::ETextureType getVrTextureType() - { - return vr::TextureType_DirectX12; - } - -#elif defined FALCOR_VK - static vr::VRVulkanTextureData_t prepareSubmitData(const Texture::SharedConstPtr& pTex, RenderContext* pRenderCtx) - { - vr::VRVulkanTextureData_t data; - data.m_nImage = (uint64_t)(VkImage)pTex->getApiHandle(); - data.m_pDevice = gpDevice->getApiHandle(); - data.m_pPhysicalDevice = gpDevice->getApiHandle(); - data.m_pInstance = gpDevice->getApiHandle(); - data.m_pQueue = pRenderCtx->getLowLevelData()->getCommandQueue(); - data.m_nQueueFamilyIndex = gpDevice->getApiCommandQueueType(LowLevelContextData::CommandQueueType::Direct); - data.m_nWidth = pTex->getWidth(); - data.m_nHeight = pTex->getHeight(); - data.m_nFormat = getVkFormat(pTex->getFormat()); - data.m_nSampleCount = pTex->getSampleCount(); - - return data; - } - - static vr::ETextureType getVrTextureType() - { - return vr::TextureType_Vulkan; - } -#else -#error VRSystem doesnt support the selected API backend -#endif - - - VRSystem* VRSystem::spVrSystem = nullptr; - - // Private default constructor - VRSystem::VRSystem() : mpHMD(0), mpCompositor(0), mLastError(0), mReadyToRender(false) - { - } - - VRSystem* VRSystem::start(bool enableVSync) - { - if(spVrSystem) - { - logWarning("Trying to reinitialize the VR system. Call is ignored"); - return spVrSystem; - } - - // Create our VRSystem object and apply developer-specified parameters - spVrSystem = new VRSystem; - spVrSystem->mVSyncEnabled = enableVSync; - - // Initialize the HMD system and check for initialization errors - vr::HmdError hmdError; - spVrSystem->mpHMD = vr::VR_Init(&hmdError, vr::VRApplication_Scene); - if(spVrSystem->mpHMD == NULL) - { - spVrSystem->mLastError = 1; // FIX - spVrSystem->mLastErrorMsg = std::string(VR_GetVRInitErrorAsEnglishDescription(hmdError)); - cleanup(); - return spVrSystem; - } - - // Initialize our compositor - hmdError = vr::VRInitError_None; - spVrSystem->mpCompositor = (vr::IVRCompositor*)vr::VR_GetGenericInterface(vr::IVRCompositor_Version, &hmdError); - if(hmdError != vr::VRInitError_None) - { - spVrSystem->mLastError = 2; // FIX - spVrSystem->mLastErrorMsg = std::string(VR_GetVRInitErrorAsEnglishDescription(hmdError)); - cleanup(); - return spVrSystem; - } - - // // IVRCompositor::GetLastError has been removed. Errors are reported in the log. - //// Check if the compositor has any error message to show - //uint32_t errStrSize = spVrSystem->mpCompositor->GetLastError( NULL, 0 ); - //if (errStrSize > 1) - //{ - // char *buf = (char *)malloc( errStrSize ); - // spVrSystem->mpCompositor->GetLastError( buf, errStrSize ); - // spVrSystem->mLastError = 3; // FIX - // spVrSystem->mLastErrorMsg = std::string("Compositor Init Error: ") + std::string(buf); - // free( buf ); - // return spVrSystem; - //} - - // Initialize the class that can describe our play-size area. If this fails, it's not fatal... We just won't - // have any idea if our HMD has been set up or what the size of our play area is. - hmdError = vr::VRInitError_None; - spVrSystem->mpChaperone = (vr::IVRChaperone *)vr::VR_GetGenericInterface(vr::IVRChaperone_Version, &hmdError); - if(!spVrSystem->mpChaperone) - { - spVrSystem->mpChaperone = 0; - } - - vr::HmdColor_t col; - col.r = 1.0f; - col.g = 0.0f; - col.b = 0.0f; - col.a = 1.0f; - spVrSystem->mpChaperone->SetSceneColor(col); - - // Initialize the class that can provide renderable models of controllers, etc. If this fails, it's non-fatal, - // we just won't have access to internal geometry. - spVrSystem->mpModels = (vr::IVRRenderModels *)vr::VR_GetGenericInterface(vr::IVRRenderModels_Version, &hmdError); - if(!spVrSystem->mpModels) - { - spVrSystem->mpModels = 0; - } - - // Create a lens distortion VBO if we're in GL mode - spVrSystem->createDistortionVBO(); - - // Allocate some data we'll need over the lifetime of this object - spVrSystem->mDevicePoses = new vr::TrackedDevicePose_t[vr::k_unMaxTrackedDeviceCount]; - if(!spVrSystem->mDevicePoses) - { - spVrSystem->mLastError = 4; // FIX - spVrSystem->mLastErrorMsg = "Memory allocation error in VRSystem::create()!"; - cleanup(); - return spVrSystem; - } - - return spVrSystem; - } - - void VRSystem::initDisplayAndController() - { - // Create a display/hmd object for our system - spVrSystem->mDisplay = VRDisplay::create(spVrSystem->mpHMD, spVrSystem->mpModels); - spVrSystem->mpHMDModel = spVrSystem->mDisplay->getRenderableModel(); - - // Create controllers for our system - spVrSystem->mControllers[0] = VRController::create(spVrSystem->mpHMD, spVrSystem->mpModels); - spVrSystem->mControllers[1] = VRController::create(spVrSystem->mpHMD, spVrSystem->mpModels); - - // Create lighthouse trackers for our system - spVrSystem->mTracker[0] = VRTrackerBox::create(spVrSystem->mpHMD, spVrSystem->mpModels); - spVrSystem->mTracker[1] = VRTrackerBox::create(spVrSystem->mpHMD, spVrSystem->mpModels); - - // Create a play area - spVrSystem->mPlayArea = VRPlayArea::create(spVrSystem->mpHMD, spVrSystem->mpChaperone); - - // All right! Done with basic setup. If we get this far, we should be ready and able to render - // (even if we have issues with controllers, etc). - spVrSystem->mReadyToRender = true; - } - - void VRSystem::cleanup(void) - { - if(spVrSystem) - { - if (spVrSystem->mDevicePoses) - { - delete [] spVrSystem->mDevicePoses; - spVrSystem->mDevicePoses = nullptr; - } - vr::VR_Shutdown(); - safe_delete(spVrSystem); - } - } - - bool VRSystem::isReady(void) - { - return mReadyToRender; - } - - - uint32_t VRSystem::getError(std::string *errMessage) - { - if(errMessage != NULL) - { - *errMessage = mLastErrorMsg; - mLastErrorMsg = ""; - } - uint32_t tmpError = mLastError; - mLastError = 0; - return tmpError; - } - - - VRController::SharedPtr VRSystem::getControllerFromDeviceID(int32_t devID) - { - if(mControllers[0]->getDeviceID() == devID || mControllers[0]->getDeviceID() < 0) return mControllers[0]; - if(mControllers[1]->getDeviceID() == devID || mControllers[1]->getDeviceID() < 0) return mControllers[1]; - return nullptr; - } - - VRTrackerBox::SharedPtr VRSystem::getTrackerFromDeviceID(int32_t devID) - { - if(mTracker[0]->getDeviceID() == devID) return mTracker[0]; - if(mTracker[1]->getDeviceID() == devID) return mTracker[1]; - return nullptr; - } - - bool VRSystem::pollEvents(void) - { - bool processed = false; - if(!mpHMD) return processed; - - vr::VREvent_t event; - vr::TrackedDeviceClass curDevice; - while(mpHMD->PollNextEvent(&event, sizeof(event))) - { - // Figure out what type of device we've got creating events - curDevice = mpHMD->GetTrackedDeviceClass(event.trackedDeviceIndex); - - VRController::SharedPtr pController; - VRTrackerBox::SharedPtr pTracker; - - if (curDevice == vr::TrackedDeviceClass_Controller) - { - pController = getControllerFromDeviceID(event.trackedDeviceIndex); - } - if (curDevice == vr::TrackedDeviceClass_TrackingReference) - { - pTracker = getTrackerFromDeviceID(event.trackedDeviceIndex); - } - - - // Process the event - processed = true; - switch(event.eventType) - { - // If we just attached a controller, get a representative model to use to display it. - case vr::VREvent_TrackedDeviceActivated: - // Make sure we have a model to render controllers - if(curDevice == vr::TrackedDeviceClass_Controller) - { - // Ensure we attach our controllers to the appropriate OpenVR IDs, if we've never seen this controller before - if(!pController) - { - pController = (mControllers[0]->getDeviceID() < 0) ? mControllers[0] : mControllers[1]; - pController->updateOnActivate(event.trackedDeviceIndex); - } - - // Check to see if we have a contoller model yet. If not, get one. - if(!mpControlModel || !mpAxisModel) - { - mpControlModel = pController->getRenderableModel(); - mpAxisModel = pController->getRenderableAxes(); - } - - printf("Controller %d activated; OpenVR device ID %d\n", (mControllers[0]->getDeviceID() == event.trackedDeviceIndex ? 0 : 1), event.trackedDeviceIndex); - } - else if(curDevice == vr::TrackedDeviceClass_TrackingReference) - { - // Ensure we attach our tracker to the appropriate OpenVR IDs, if we've never seen this controller before - if(!pTracker) - { - pTracker = (mTracker[0]->getDeviceID() < 0) ? mTracker[0] : mTracker[1]; - pTracker->updateOnActivate(event.trackedDeviceIndex); - } - - // Check to see if we have a tracker/lighthouse model yet. If not, get one. - if(!mpLighthouseModel) - { -// mpLighthouseModel = pTracker->getRenderableModel(); - } - - printf("Lighthouse tracker %d activated; OpenVR device ID %d\n", (mTracker[0]->getDeviceID() == event.trackedDeviceIndex ? 0 : 1), event.trackedDeviceIndex); - } - break; - case vr::VREvent_TrackedDeviceDeactivated: - if(pController) - { - pController->updateActivatedState(false); - } - break; - case vr::VREvent_TrackedDeviceUpdated: - break; - case vr::VREvent_ButtonPress: - if(pController) - { - pController->updateOnButtonEvent(1ull << event.data.controller.button, VRController::ButtonEvent::Press); - } - break; - case vr::VREvent_ButtonUnpress: - if(pController) - { - pController->updateOnButtonEvent(1ull << event.data.controller.button, VRController::ButtonEvent::Unpress); - } - break; - case vr::VREvent_ButtonTouch: - if(pController) - { - pController->updateOnButtonEvent(1ull << event.data.controller.button, VRController::ButtonEvent::Touch); - } - break; - case vr::VREvent_ButtonUntouch: - if(pController) - { - pController->updateOnButtonEvent(1ull << event.data.controller.button, VRController::ButtonEvent::Untouch); - } - break; - } - } - - return processed; - } - - void VRSystem::refreshTracking(void) - { - if(!mpHMD || !mpCompositor) return; - - // Query the compositor to return current positions of all tracked devices - mpCompositor->WaitGetPoses(mDevicePoses, vr::k_unMaxTrackedDeviceCount, NULL, 0); - - // Now that we have the poses for all of our devices, cycle through them to update their state - for(uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) - { - if(mpHMD->IsTrackedDeviceConnected(i)) - { - vr::TrackedDeviceClass curDevice = mpHMD->GetTrackedDeviceClass(i); - if(curDevice == vr::TrackedDeviceClass_HMD) - { - if(mDisplay) - { - mDisplay->updateOnNewPose(&mDevicePoses[i]); // vr::k_unTrackedDeviceIndex_Hmd] ); - } - } - else if(curDevice == vr::TrackedDeviceClass_Controller) - { - // Determine which contoller this is - VRController::SharedPtr curController = getControllerFromDeviceID(i); - if(curController) - { - curController->updateOnNewPose(&mDevicePoses[i], i); - } - } - else if(curDevice == vr::TrackedDeviceClass_TrackingReference) - { - VRTrackerBox::SharedPtr curTracker = getTrackerFromDeviceID(i); - if(curTracker) - { - curTracker->updateOnNewPose(&mDevicePoses[i], i); - } - } - - // Other types of devices? - } - } - - } - - void VRSystem::refresh() - { - if (isReady()) - { - // Get the VR data - pollEvents(); - refreshTracking(); - } - } - - bool VRSystem::submit(VRDisplay::Eye whichEye, const Texture::SharedConstPtr& pDisplayTex, RenderContext* pRenderCtx) - { - if (!mpCompositor) return false; - - auto submitTex = prepareSubmitData(pDisplayTex, pRenderCtx); - vr::Texture_t subTex; - subTex.eType = getVrTextureType(); - subTex.handle = &submitTex; - subTex.eColorSpace = isSrgbFormat(pDisplayTex->getFormat()) ? vr::EColorSpace::ColorSpace_Gamma : vr::EColorSpace::ColorSpace_Linear; - - mpCompositor->Submit((whichEye == VRDisplay::Eye::Right) ? vr::Eye_Right : vr::Eye_Left, &subTex, NULL); - return true; - } - - - // Create a VBO that can be used in a separate pass to transform a flat image into the distorted image needed to - // be displayed on the HMD screen. The compositor already does this for you, but if you want to see the - // distorted image yourself on your non-HMD screen, you'll need this to do the transformation. - void VRSystem::createDistortionVBO(void) - { - //// Declare internal structure type only needed in this method... - //struct VertexDataLens - //{ - // float position[2]; - // float texCoordRed[2]; - // float texCoordGreen[2]; - // float texCoordBlue[2]; - //}; - - //// HACK! This may only work for OpenGL renderers... TBD. (This code came from the OpenVR OpenGL sample app.) - //if(!mpHMD) return; - - //uint32_t m_iLensGridSegmentCountH = 43; - //uint32_t m_iLensGridSegmentCountV = 43; - - //float w = (float)(1.0 / float(m_iLensGridSegmentCountH - 1)); - //float h = (float)(1.0 / float(m_iLensGridSegmentCountV - 1)); - //float u, v = 0; - //std::vector vVerts(0); - //std::vector vIndices; - //uint32_t a, b, c, d; - - //// Distortion vertex positions - //for(int eye = 0; eye < 2; eye++) - //{ - // float Xoffset = (eye == 0) ? -1.0f : 0.0f; - // for(int y = 0; yComputeDistortion((eye == 0) ? vr::Eye_Left : vr::Eye_Right, u, v); - // vVerts.push_back({{Xoffset + u, -1 + 2 * y*h}, {dc0.rfRed[0], 1 - dc0.rfRed[1]}, {dc0.rfGreen[0], 1 - dc0.rfGreen[1]}, {dc0.rfBlue[0], 1 - dc0.rfBlue[1]}}); - // } - // } - - // uint32_t offset = (eye == 0) ? 0 : (m_iLensGridSegmentCountH)*(m_iLensGridSegmentCountV); - // for(uint32_t y = 0; y < m_iLensGridSegmentCountV - 1; y++) - // { - // for(uint32_t x = 0; x < m_iLensGridSegmentCountH - 1; x++) - // { - // a = m_iLensGridSegmentCountH*y + x + offset; - // b = m_iLensGridSegmentCountH*y + x + 1 + offset; - // c = (y + 1)*m_iLensGridSegmentCountH + x + 1 + offset; - // d = (y + 1)*m_iLensGridSegmentCountH + x + offset; - // vIndices.push_back(a); - // vIndices.push_back(b); - // vIndices.push_back(c); - - // vIndices.push_back(a); - // vIndices.push_back(c); - // vIndices.push_back(d); - // } - // } - //} - - //SimpleModelImporter::VertexFormat vaoLayout; - //vaoLayout.attribs.push_back({SimpleModelImporter::AttribType::Position, 2, AttribFormat::AttribFormat_F32}); - //vaoLayout.attribs.push_back({SimpleModelImporter::AttribType::User0, 2, AttribFormat::AttribFormat_F32}); // texcoord red - //vaoLayout.attribs.push_back({SimpleModelImporter::AttribType::User1, 2, AttribFormat::AttribFormat_F32}); // texcoord green - //vaoLayout.attribs.push_back({SimpleModelImporter::AttribType::User2, 2, AttribFormat::AttribFormat_F32}); // texcoord blue - //mpLensDistortModel[0] = SimpleModelImporter::create(vaoLayout, - // uint32_t(vVerts.size()*sizeof(VertexDataLens)), &vVerts[0], - // uint32_t(vIndices.size()*sizeof(uint32_t) / 2), &vIndices[0]); - //mpLensDistortModel[1] = SimpleModelImporter::create(vaoLayout, - // uint32_t(vVerts.size()*sizeof(VertexDataLens)), &vVerts[0], - // uint32_t(vIndices.size()*sizeof(uint32_t) / 2), &vIndices[vIndices.size() / 2]); - - } - - bool VRSystem::getTimeSinceLastVsync(float *pfSecondsSinceLastVsync, uint64_t *pulFrameCounter) - { - return mpHMD->GetTimeSinceLastVsync(pfSecondsSinceLastVsync, pulFrameCounter); - } - -#ifdef _DEBUG - void VRSystem::checkInit() - { - if(spVrSystem == nullptr) - { - logWarning("VR system not initialized"); - } - } -#endif - -#ifdef FALCOR_VK - std::vector VRSystem::getRequiredVkInstanceExtensions() - { - uint32_t size = spVrSystem->mpCompositor->GetVulkanInstanceExtensionsRequired(nullptr, 0); - std::vector charVec(size); - spVrSystem->mpCompositor->GetVulkanInstanceExtensionsRequired(charVec.data(), size); - std::string str(charVec.data()); - - std::vector ext = splitString(str, " "); - return ext; - } - - std::vector VRSystem::getRequiredVkDeviceExtensions(VkPhysicalDevice device) - { - uint32_t size = spVrSystem->mpCompositor->GetVulkanDeviceExtensionsRequired(device, nullptr, 0); - std::vector charVec(size); - spVrSystem->mpCompositor->GetVulkanDeviceExtensionsRequired(device, charVec.data(), size); - std::string str(charVec.data()); - - std::vector ext = splitString(str, " "); - return ext; - } -#endif -} \ No newline at end of file diff --git a/Framework/Source/VR/OpenVR/VRSystem.h b/Framework/Source/VR/OpenVR/VRSystem.h deleted file mode 100644 index 3c45054a1..000000000 --- a/Framework/Source/VR/OpenVR/VRSystem.h +++ /dev/null @@ -1,257 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -// This is the main class for incorporating basic VR support into your application -// via OpenVR. This is currently tested only on the HTC Vive headset. -// -// Basic initialization: -// VRSystem::UniquePtr vrSys = VRSystem::create( appRenderContext ); -// -// Basic render loop: -// if (vrSys->isReady()) { -// vrSys->pollEvents(); // Check for button presses -// vrSys->refreshTracking(); // Update HMD/controller positions -// RenderEyeImages( &myLeftEyeFbo, &myRightEyeFbo ); // Do your rendering -// vrSys->submit( Eye::Left, myLeftEyeFbo ); // Submit images to HMD -// vrSys->submit( Eye::Right, myRightEyeFbo ); -// } -// -// Objects for the controller, hmd, tracker, and room are instantiated by this main -// wrapper and are available via the appropriate get*() accessors. These objects allow -// access to more specific details needed for rendering. -// -// -// Chris Wyman (12/15/2015) - -#pragma once - -#include "Framework.h" -#include "Graphics/Model/Model.h" -#include "Graphics/Model/Loaders/SimpleModelImporter.h" -#include "glm/mat4x4.hpp" -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "API/FBO.h" -#include "API/RenderContext.h" -#include "Graphics/Program/Program.h" -#include "API/Texture.h" -#include "VRController.h" -#include "VRTrackerBox.h" -#include "VRDisplay.h" -#include "VRPlayArea.h" - -#pragma comment(lib, "openvr_api.lib") - -// Forward declare OpenVR system class types to remove "openvr.h" dependencies from the headers -namespace vr { - class IVRSystem; - class IVRCompositor; - class IVRRenderModels; - class IVRChaperone; - struct TrackedDevicePose_t; -} - -namespace Falcor -{ - /** High-level abstraction for a basic interface to OpenVR - */ - class VRSystem - { - public: - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Constructor and destructor methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - /** Create an OpenVR system class and initialize the system. - If using Vulkan, this function needs to be called before creating VkInstance, so that we can retrieve the required extensions. - For D3D12 this can be called after the device was created - */ - static VRSystem* start( bool enableVSync = true ); - static VRSystem* instance() { checkInit(); return spVrSystem; } - - // If you want to clean up resources and shut down the VR system, call cleanup(), which acts as - // a destructor. This wrapper does not have an explicit destructor, since Falcor appears - // to do something under the hood with such a destructor that either destroys things twice - // or destroys them in an inappropriate order when exiting the program. (TODO: Debug?) - static void cleanup(void); - - /** Initialize the display and the controllers - */ - void initDisplayAndController(); - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Simple accessors - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Is the VR HMD initialized and read to render? If false, go grab errors via getError() - bool isReady(); - - // Is VSync currently enabled? (TODO: Add toggle. But mostly you just want vSync always ON.) - bool isVSyncEnabled() const { return mVSyncEnabled; } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Basic wrapper usage methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Query the HMD for the current position of tracked devices - void refreshTracking(); - - // Poll for events (device [de]activate, button presses, etc). Returns true if event(s) occurs. - // Updates internal state (i.e., in the HMD, controller, tracker classes) to reflect these events - bool pollEvents(); - - // Refresh the HMD. This is just a wrapper which calls refreshTracking() and pollEvents() - void refresh(); - - // Submit rendered frames to the HMD. Should submit one image to left eye and one to right eye - // each frame. The submit() routines handle all warping due to lens and color distortions. - // Size of each texture should be: getHMD()->getRecommendedRenderSize() for best results. - bool submit( VRDisplay::Eye whichEye, const Texture::SharedConstPtr& pDisplayTex, RenderContext* pRenderCtx); - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Access current world state (positions of HMD, controllers, etc) - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Get our head mounted display object. Includes rendering matrices and target size. - VRDisplay::SharedPtr getHMD() { return mDisplay; } - - // Get our controller(s). Includes position, button state, aim, etc. - VRController::SharedPtr getController( uint32_t idx ) { return (idx < 2) ? mControllers[idx] : nullptr; } - - // Get our tracker(s)/lighthouse(s). - VRTrackerBox::SharedPtr getTracker( uint32_t idx ) { return (idx < 2) ? mTracker[idx] : nullptr; } - - // Get information about our room/play area. - VRPlayArea::SharedPtr getPlayArea() { return mPlayArea; } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Access renderable geometry. These are shortcuts (can also be accessed through controller, hmd, - // tracker classes). Can be nullptr if devices haven't been activated or the OpenVR DLL has - // not built-in model for them. - ////////////////////////////////////////////////////////////////////////////////////////////////// - - Model::SharedPtr getControllerModel() { return mpControlModel; } - Model::SharedPtr getTrackerModel() { return mpLighthouseModel; } - Model::SharedPtr getHMDModel() { return mpHMDModel; } - - // This is an extra, added by Chris, for a simple xyz axis model for controllers. - Model::SharedPtr getAxesModel() { return mpAxisModel; } - - // Gets a very simple Falcor model representing the HMD's lens distortion. - // -> AttirbuteLocation 'Position' is position. 2 components directly representing NDC. - // -> AttirbuteLocation 'User0' is the red distortion. (I.e., offset into original texture for red channel) - // -> AttirbuteLocation 'User1' is the green distortion. (I.e., offset into original texture for green channel) - // -> AttirbuteLocation 'User2' is the blue distortion. (I.e., offset into original texture for blue channel) - // Note: This model currently cannot be used with Falcor's ModelRenderer. Why needs to be debugged. - Model::SharedPtr getDistortionModel( VRDisplay::Eye whichEye ) { return mpLensDistortModel[(whichEye == VRDisplay::Eye::Left) ? 0 : 1]; } - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Error routines - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Pretty rudimentary error messages. Mostly useful for initialization/constructor failures, currently. - uint32_t getError( std::string *errMessage = NULL ); - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Enable for low level interactions with OpenVR - ////////////////////////////////////////////////////////////////////////////////////////////////// - - //vr::IVRSystem *getOpenVRSystemPtr(void) const { return mpHMD; } - vr::IVRCompositor *getOpenVRCompositor(void) const { return mpCompositor; } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Display/Timing specific methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - bool getTimeSinceLastVsync(float *pfSecondsSinceLastVsync, uint64_t *pulFrameCounter); - -#ifdef FALCOR_VK - static std::vector getRequiredVkInstanceExtensions(); - static std::vector getRequiredVkDeviceExtensions(VkPhysicalDevice device); -#endif - private: - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Declare private stuff.. - ////////////////////////////////////////////////////////////////////////////////////////////////// - - VRSystem(); - static VRSystem* spVrSystem; - - // Returns a controller/tracker from an OpenVR device ID. Returns nullptr if unknown deviceID. - VRController::SharedPtr getControllerFromDeviceID( int32_t devID ); - VRTrackerBox::SharedPtr getTrackerFromDeviceID( int32_t devID ); - - // System parameters - bool mReadyToRender; - bool mVSyncEnabled; - uint32_t mRenderAPI; - - // Base OpenVR system objects - vr::IVRSystem *mpHMD; - vr::IVRCompositor *mpCompositor; - vr::IVRRenderModels *mpModels; - vr::IVRChaperone *mpChaperone; - - // A place to stash OpenVR's most recent error information - uint32_t mLastError; // TODO: convert to enum - std::string mLastErrorMsg; - - // An array of poses we'll pass to OpenVR internally; - vr::TrackedDevicePose_t *mDevicePoses = nullptr; - - // Some experimental display stuff concerning the HMD's image distortion - void createDistortionVBO(); - - // Our sub-classes that store state for controllers, hmds, trackers, and the play area - VRController::SharedPtr mControllers[2]; - VRTrackerBox::SharedPtr mTracker[2]; - VRDisplay::SharedPtr mDisplay; - VRPlayArea::SharedPtr mPlayArea; - - // Renderable models. We initialize these as soon as we know we have valid instances - // of controllers, trackers, and HMDs. This makes it a bit easier than querying - // repeatedly or manually processing events. - Model::SharedPtr mpControlModel; - Model::SharedPtr mpLighthouseModel; - Model::SharedPtr mpAxisModel; - Model::SharedPtr mpHMDModel; - - // A representation of the lens distortion for the 2 eyes (either to display on screen, or - // to render to the HMD if you're not using the IVRSystem's compositor. However, this - // wrapper only exposes the IVRSystem's compositor right now. - Model::SharedPtr mpLensDistortModel[2]; - -#ifdef _DEBUG - static void checkInit(); -#else - static void checkInit() {}; -#endif - }; -} diff --git a/Framework/Source/VR/OpenVR/VRTrackerBox.cpp b/Framework/Source/VR/OpenVR/VRTrackerBox.cpp deleted file mode 100644 index 79b07161b..000000000 --- a/Framework/Source/VR/OpenVR/VRTrackerBox.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "FalcorConfig.h" -#include "VRSystem.h" -#include "openvr.h" -#include "VRController.h" -#include "VRTrackerBox.h" - -using namespace Falcor; - -// Private namespace -namespace -{ - // Matrix to convert OpenVR matrix to OpenGL matrix. - glm::mat4 convertOpenVRMatrix34( vr::HmdMatrix34_t mat ) - { - return glm::mat4( mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, - mat.m[0][1], mat.m[1][1], mat.m[2][1], 0.0f, - mat.m[0][2], mat.m[1][2], mat.m[2][2], 0.0f, - mat.m[0][3], mat.m[1][3], mat.m[2][3], 1.0f ); - } -} - - -VRTrackerBox::SharedPtr VRTrackerBox::create( vr::IVRSystem *vrSys, vr::IVRRenderModels *modelClass ) -{ - SharedPtr ctrl = SharedPtr( new VRTrackerBox ); - - // Setup default tracker (inactive) - ctrl->mIsActive = false; - ctrl->mTrackerCenter = glm::vec3( 0.0f ); - ctrl->mWorldMatrix = glm::mat4(); - - // Setup internal OpenVR state - ctrl->mpVrSys = vrSys; - ctrl->mpRenderModels = modelClass; - ctrl->mDeviceID = -1; // not yet known. - - return ctrl; -} - -Model::SharedPtr VRTrackerBox::getRenderableModel( Texture::SharedPtr overrideTexture ) -{ - // If we already got a model, it won't change. so go ahead and return the same one. - if ( mpRenderableModel ) - { - return mpRenderableModel; - } - - // Go ahead and pull our model from the OpenVR system - vr::RenderModel_t* pModel = nullptr; - - vr::EVRRenderModelError modelErr; - do - { - modelErr = mpRenderModels->LoadRenderModel_Async(mModelName.c_str(), &pModel); - } - while(modelErr == vr::VRRenderModelError_Loading); - - if (modelErr != vr::VRRenderModelError_None || !pModel) - return nullptr; - - // create and populate the texture - mpModelTexture = overrideTexture; - vr::RenderModel_TextureMap_t *pTexture = nullptr; - - vr::EVRRenderModelError texErr; - do - { - texErr = mpRenderModels->LoadTexture_Async(pModel->diffuseTextureId, &pTexture); - }while(texErr == vr::VRRenderModelError_Loading); - - if (texErr != vr::VRRenderModelError_None || !pTexture) - return nullptr; - - if ( !overrideTexture ) - mpModelTexture = Texture::create2D( pTexture->unWidth, pTexture->unHeight, - ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, - (const void *) pTexture->rubTextureMapData ); - - // OpenVR models use uint16_t index buffers. Convert to uint32_t for Falcor. - uint32_t *newIdxBuf = (uint32_t *) malloc( pModel->unTriangleCount * 3 * sizeof( uint32_t ) ); - for ( uint32_t i = 0; i < pModel->unTriangleCount * 3; i++ ) - newIdxBuf[i] = pModel->rIndexData[i]; - - // Use the SimpleModelImporter to create a Falcor model from memory. - SimpleModelImporter::VertexFormat vertLayout; - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::Position, 3, AttribFormat::AttribFormat_F32 } ); - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::Normal, 3, AttribFormat::AttribFormat_F32 } ); - vertLayout.attribs.push_back( { SimpleModelImporter::AttribType::TexCoord, 2, AttribFormat::AttribFormat_F32 } ); - Model::SharedPtr ctrlModel = SimpleModelImporter::create( vertLayout, - sizeof( vr::RenderModel_Vertex_t ) * pModel->unVertexCount, - pModel->rVertexData, - pModel->unTriangleCount * 3 * sizeof( uint32_t ), - newIdxBuf, - mpModelTexture ); - - // Free our temporary memory - free( newIdxBuf ); - - return ctrlModel; -} - -void VRTrackerBox::updateOnActivate( int32_t deviceID ) -{ - // Remember which device ID we are (though this is transient; OpenVR changes it as new devices are added) - mDeviceID = deviceID; - mIsActive = true; - - // See if this device has a renderable model - uint32_t unRequiredBufferLen = mpVrSys->GetStringTrackedDeviceProperty( mDeviceID, vr::Prop_RenderModelName_String, NULL, 0, NULL ); - if ( unRequiredBufferLen != 0 ) - { - // If so, get a string describing it - char *pchBuffer = new char[unRequiredBufferLen]; - unRequiredBufferLen = mpVrSys->GetStringTrackedDeviceProperty( mDeviceID, vr::Prop_RenderModelName_String, pchBuffer, unRequiredBufferLen, NULL ); - mModelName = pchBuffer; - delete[] pchBuffer; - } -} - -void VRTrackerBox::updateOnNewPose( vr::TrackedDevicePose_t *newPose, int32_t deviceID ) -{ - // Can't update pose if things aren't initialized properly - if ( !mpVrSys || !newPose ) return; - - // Set some internal active/inactive state - mIsActive = newPose->bDeviceIsConnected && newPose->bPoseIsValid; - mDeviceID = deviceID; - - // Nothing is really valid beyond this point... - if ( !newPose->bPoseIsValid ) return; - - mWorldMatrix = convertOpenVRMatrix34( newPose->mDeviceToAbsoluteTracking ); - mTrackerCenter = glm::vec3( mWorldMatrix * glm::vec4( 0, 0, 0, 1 ) ); -} diff --git a/Framework/Source/VR/OpenVR/VRTrackerBox.h b/Framework/Source/VR/OpenVR/VRTrackerBox.h deleted file mode 100644 index 9dcb7bdf8..000000000 --- a/Framework/Source/VR/OpenVR/VRTrackerBox.h +++ /dev/null @@ -1,137 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -// This is wrapper class around OpenVR tracker boxes (i.e., Vive's Lighthouses). -// It gives position, active status, and a renderable model of the tracker. -// -// Chris Wyman (12/15/2015) - -#pragma once - -#include "Framework.h" -#include "glm/mat4x4.hpp" -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" -#include "API/FBO.h" -#include "API/RenderContext.h" -#include "Graphics/Program/Program.h" -#include "API/Texture.h" -#include "Graphics/Model/Model.h" - - -// Forward declare OpenVR system class types to remove "openvr.h" dependencies from Falcor headers -namespace vr -{ - class IVRSystem; // A generic system with basic API to talk with a VR system - class IVRCompositor; // A class that composite rendered results with appropriate distortion into the HMD - class IVRRenderModels; // A class giving access to renderable geometry for things like controllers - class IVRChaperone; - class IVRRenderModels; - struct TrackedDevicePose_t; -} - - -namespace Falcor -{ - // Forward declare the VRSystem class to avoid header cycles. - class VRSystem; - - /** High-level OpenVR controller abstraction - */ - class VRTrackerBox - { - public: - // Declare standard Falcor shared pointer types. - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - // Controller constructor. Automatically called by other wrapper classes. - static SharedPtr create( vr::IVRSystem *vrSys, vr::IVRRenderModels *modelClass = 0 ); - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Simple accessor methods - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Check if the controller is active an/or tracking. - bool isActive( void ) const { return mIsActive; } - - // Get a matrix transforming from model/object to world coordinates based on tracking data - glm::mat4 getToWorldMatrix( void ) const { return mWorldMatrix; } - - // Get the center of the controller in world space - glm::vec3 getPosition( void ) const { return mTrackerCenter; } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Tracker box geometric accessors - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // Get a Falcor model representing the lighthouse tracker. All trackers will return the same geometry, - // though you *can* pass your own texture in if you'd like to override OpenVR's baked AO texture. - Model::SharedPtr getRenderableModel( Texture::SharedPtr overrideTexture = nullptr ); - - // When rendering, if you directly need the texture for the model, you can grab it here - Texture::SharedPtr getRenderableModelTexture( void ) { return mpModelTexture; } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - // Wrapper-specific methods; Shouldn't need to use outside of OpenVR wrapper code, generally. - ////////////////////////////////////////////////////////////////////////////////////////////////// - - // When the tracker detects it has moved (or it can no longer track correctly), this gets called - void updateOnEnvironmentChange( void ) {} // TBD - - // When a new controller pose is provided by OpenVR, call this to update state viewable by above - // accessors. deviceID is OpenVR's ID for the objects it tracks. - void updateOnNewPose( vr::TrackedDevicePose_t *newPose, int32_t deviceID ); - - // When the controller first activates, there'a a bunch of state info about the controller - // OpenVR has that we don't -- the name of the model inside the DLL, and other controller - // properties. When the controller is first activated, calling this method allows us - // to know what we're dealing with. Without calling this, some more advanced properties - // of this class (i.e., getting the model geometry) won't work correctly. - void updateOnActivate( int32_t deviceID ); - - // If we need to query OpenVR about this controller, you need its internal OpenVR deviceID - int32_t getDeviceID( void ) const { return mDeviceID; } - - private: - bool mIsActive; // Is the tracker currently active? - glm::vec3 mTrackerCenter; // The center of the controller - glm::mat4 mWorldMatrix; // The model-to-world matrix for the controller - - Model::SharedPtr mpRenderableModel; // A Falcor renderable model. Can be NULL if OpenVR has no model for the tracker! - Texture::SharedPtr mpModelTexture; - - // Internals for accessing OpenVR state. Not user accessible - vr::IVRRenderModels *mpRenderModels; - vr::IVRSystem *mpVrSys; - int32_t mDeviceID; - std::string mModelName; - - }; - -} diff --git a/Framework/Source/VR/VrFbo.cpp b/Framework/Source/VR/VrFbo.cpp deleted file mode 100644 index 298898bee..000000000 --- a/Framework/Source/VR/VrFbo.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "VrFbo.h" -#include "OpenVR/VRSystem.h" -#include "OpenVR/VRDisplay.h" -#include "Graphics/FboHelper.h" -#include "API/Texture.h" - -namespace Falcor -{ - glm::ivec2 getHmdRenderSize() - { - VRSystem* pVrSystem = VRSystem::instance(); - VRDisplay* pDisplay = pVrSystem->getHMD().get(); - glm::ivec2 renderSize = pDisplay->getRecommendedRenderSize(); - return renderSize; - } - - VrFbo::UniquePtr VrFbo::create(const Fbo::Desc& desc, uint32_t width, uint32_t height) - { - width = (width == 0) ? getHmdRenderSize().x : width; - height = (height == 0) ? getHmdRenderSize().y : height; - - // Create the FBO - VrFbo::UniquePtr pVrFbo = std::make_unique(); - pVrFbo->mpFbo = FboHelper::create2D(width, height, desc, 2); - - // create the textures - // in the future we should use SRVs directly - // or some other way to avoid copying resources - - pVrFbo->mpLeftView = Texture::create2D(width, height, desc.getColorTargetFormat(0),1,1); - pVrFbo->mpRightView = Texture::create2D(width, height, desc.getColorTargetFormat(0),1,1); - - return pVrFbo; - } - - void VrFbo::submitToHmd(RenderContext* pRenderCtx) const - { - VRSystem* pVrSystem = VRSystem::instance(); - - uint32_t ltSrcSubresourceIdx = mpFbo->getColorTexture(0)->getSubresourceIndex(0, 0); - uint32_t rtSrcSubresourceIdx = mpFbo->getColorTexture(0)->getSubresourceIndex(1, 0); - - uint32_t ltDstSubresourceIdx = mpLeftView->getSubresourceIndex(0, 0); - uint32_t rtDstSubresourceIdx = mpRightView->getSubresourceIndex(0, 0); - - pRenderCtx->copySubresource(mpLeftView.get(), ltDstSubresourceIdx, mpFbo->getColorTexture(0).get(), ltSrcSubresourceIdx); - pRenderCtx->copySubresource(mpRightView.get(), rtDstSubresourceIdx, mpFbo->getColorTexture(0).get(), rtSrcSubresourceIdx); - - pVrSystem->submit(VRDisplay::Eye::Left, mpLeftView, pRenderCtx); - pVrSystem->submit(VRDisplay::Eye::Right, mpRightView, pRenderCtx); - } -} \ No newline at end of file diff --git a/Makefile b/Makefile index 3380ae9b2..f778099d0 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ $(shell pkg-config --cflags libavcodec libavdevice libavformat libswscale libavu ADDITIONAL_LIB_DIRS = -L "Bin/" \ -L "Framework/Externals/OpenVR/lib" \ --L "Framework/Externals/Slang/bin/linux-x64/release" \ +-L "Framework/Externals/Slang/bin/linux-x86_64/release" \ -L "$(VULKAN_SDK)/lib" LIBS = -lfalcor -lfalcorshared \ @@ -112,7 +112,7 @@ COMMON_FLAGS=-c -Wall -Werror -std=c++17 -m64 $(DISABLED_WARNINGS) # Defines DEBUG_DEFINES:=-D "_DEBUG" RELEASE_DEFINES:= -COMMON_DEFINES:=-D "FALCOR_VK" -D "GLM_FORCE_DEPTH_ZERO_TO_ONE" -D "_PROJECT_DIR_=\"Framework/Source\"" +COMMON_DEFINES:=-D "FALCOR_VK" -D "GLM_FORCE_DEPTH_ZERO_TO_ONE" -D "_PROJECT_DIR_=\"Framework/Source\"" $(TESTS) # Base source directory SOURCE_DIR:=Framework/Source/ @@ -161,7 +161,7 @@ endef define MoveFalcorData $(call MoveProjectData,Framework/Source/,$(1)) @cp -r Framework/Source/ShadingUtils/* $(1)/Data/ - @cp Framework/Externals/Slang/bin/linux-x64/release/*.so $(1) + @cp Framework/Externals/Slang/bin/linux-x86_64/release/*.so $(1) endef # Copies the "Data" folder inside the directory specified by Source path to the Destination path diff --git a/Mogwai.sln b/Mogwai.sln new file mode 100644 index 000000000..6e118f1d3 --- /dev/null +++ b/Mogwai.sln @@ -0,0 +1,70 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{350A0B15-98C0-45E3-872B-4FEFB47AA37C}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Falcor", "Source\Falcor\Falcor.vcxproj", "{2C535635-E4C5-4098-A928-574F0E7CD5F9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildScripts", "BuildScripts", "{0ABDD59E-937B-41FF-B78A-7F47CBCCA387}" + ProjectSection(SolutionItems) = preProject + Build\deploycommon.bat = Build\deploycommon.bat + Build\deployproject.bat = Build\deployproject.bat + Build\patchpropssheet.py = Build\patchpropssheet.py + Build\prebuild.bat = Build\prebuild.bat + Build\update_dependencies.bat = Build\update_dependencies.bat + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mogwai", "Source\Mogwai\Mogwai.vcxproj", "{204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}" + ProjectSection(ProjectDependencies) = postProject + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71} = {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderGraphEditor", "Source\Tools\RenderGraphEditor\RenderGraphEditor.vcxproj", "{DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + DebugD3D12|x64 = DebugD3D12|x64 + DebugVK|x64 = DebugVK|x64 + ReleaseD3D12|x64 = ReleaseD3D12|x64 + ReleaseVK|x64 = ReleaseVK|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugD3D12|x64.ActiveCfg = DebugD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugD3D12|x64.Build.0 = DebugD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugVK|x64.ActiveCfg = DebugVK|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.DebugVK|x64.Build.0 = DebugVK|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseD3D12|x64.ActiveCfg = ReleaseD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseD3D12|x64.Build.0 = ReleaseD3D12|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseVK|x64.ActiveCfg = ReleaseVK|x64 + {2C535635-E4C5-4098-A928-574F0E7CD5F9}.ReleaseVK|x64.Build.0 = ReleaseVK|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugD3D12|x64.Build.0 = Debug|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugVK|x64.ActiveCfg = Debug|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.DebugVK|x64.Build.0 = Debug|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseD3D12|x64.Build.0 = Release|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseVK|x64.ActiveCfg = Release|x64 + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49}.ReleaseVK|x64.Build.0 = Release|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugD3D12|x64.ActiveCfg = Debug|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugD3D12|x64.Build.0 = Debug|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugVK|x64.ActiveCfg = Debug|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.DebugVK|x64.Build.0 = Debug|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseD3D12|x64.ActiveCfg = Release|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseD3D12|x64.Build.0 = Release|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseVK|x64.ActiveCfg = Release|x64 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71}.ReleaseVK|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0ABDD59E-937B-41FF-B78A-7F47CBCCA387} = {350A0B15-98C0-45E3-872B-4FEFB47AA37C} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {357B2AE0-FE30-4AC6-8D41-B580232BC0DE} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index dec602b0c..a3de5b485 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Falcor 3.2 -================= +Falcor 4.0 Development Snapshot +=============================== Falcor is a real-time rendering framework supporting DirectX 12 and Vulkan. It aims to improve productivity of research and prototype projects. Its features include: @@ -8,44 +8,29 @@ Its features include: * Common rendering effects such as shadows and post-processing effects * DirectX Raytracing abstraction +Note that Falcor 4.0 is still under development. There will be more changes to the interfaces as well as new features. This is a snapshot of our development branch and doesn't represent an official version (not even alpha). +This release only supports DX12 on Windows. +The path tracer requires NVAPI. Please make sure you have it setup properly, otherwise the path-tracer won't work. You can find the instructions below. + + Prerequisites ------------------------ -- GPU that supports DirectX 12 or Vulkan -- Windows 10 RS2 (version 1703) or newer, or Ubuntu 17.10 +- A GPU which supports DirectX Raytracing, such as the NVIDIA Titan V or GeForce RTX (make sure you have the latest driver) +- Windows 10 RS5 (version 1809) or newer +- NVAPI On Windows: -- Visual Studio 2017 +- Visual Studio 2017 15.9.10 (it might not compile with older VS versions) - [Microsoft Windows SDK version 1809 (10.0.17763.0)](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive) - To run DirectX 12 applications with the debug layer enabled, you need to install the Graphics Tools optional feature. The tools version must match the OS version you are using (not to be confused with the SDK version used for building Falcor). There are 2 ways to install it: - Click the Windows button and type `Optional Features`, in the window that opens click `Add a feature` and select `Graphics Tools`. - Download an offline pacakge from [here](https://docs.microsoft.com/en-us/windows-hardware/test/hlk/windows-hardware-lab-kit#supplemental-content-for-graphics-media-and-mean-time-between-failures-mtbf-tests). Choose a ZIP file that matches the OS version you are using. The ZIP includes a document which explains how to install the graphics tools. -DirectX Raytracing -------------------------- -Falcor 3.0 added support for DirectX Raytracing. As of Falcor 3.1, special build configs are no longer required to enable these features. Simply use the `DebugD3D12` or `ReleaseD3D12` configs as you would for any other DirectX project. -The HelloDXR sample demonstrates how to use Falcor’s DXR abstraction layer. - -- Requirements: - - Windows 10 RS5 (version 1809) - - A GPU which supports DirectX Raytracing, such as the NVIDIA Titan V or GeForce RTX (make sure you have the latest driver) - -Falcor doesn’t support the DXR fallback layer. - -TensorFlow Support --------------- -Refer to the README located in the `Samples\Core\LearningWithEmbeddedPython` for instructions on how to set up your environment to use TensorFlow and Python with Falcor. - -Linux --------------- -Falcor is tested on Ubuntu 17.10, but may also work with other versions and distributions. - -To build and run Falcor on Linux: -- Install the Vulkan SDK following the instructions [HERE](https://vulkan.lunarg.com/doc/view/latest/linux/getting_started.html) -- Install additional dependencies: - - `sudo apt-get install python python3-dev libassimp-dev libglfw3-dev libgtk-3-dev libfreeimage-dev libavcodec-dev libavdevice-dev libavformat-dev libswscale-dev libavutil-dev` -- Run the `Makefile` - - To only build the library, run `make Debug` or `make Release` depending on the desired configuration - - To build samples, run `make` using the target for the sample(s) you want to build. Config can be changed by setting `SAMPLE_CONFIG` to `Debug` or `Release` +NVAPI installation +------------------ +After cloning the repository, head over to https://developer.nvidia.com/nvapi and download the latest version of NVAPI (this build is tested against version R435). +1. Extract the content of the zip file into `\Source\Externals\.packman`. If you have NVAPI version R435, you should have the `\Source\Externals\.packman\R435-developer` folder. +2. Rename `R435-developer` to `NVAPI`. You should end up with the `\Source\Externals\.packman\NVAPI` folder. Building Falcor --------------- @@ -55,10 +40,9 @@ follow the instructions below. Creating a New Project ------------------------ - If you haven't done so already, create a Visual Studio solution and project for your code. Falcor only supports 64-bit builds, so make sure you have a 64-bit build configuration -- Add `Framework\Source\Falcor.props` to your project (View -> Other Windows ->Property Manager -> Right click your project -> Add existing property sheet) -- Add `FalcorSharedObjects.vcxproj` to your solution (Located at `Framework/FalcorSharedObjects/FalcorSharedObjects.vcxproj`) +- Add `Falcor.props` to your project (Property Manager -> Right click your project -> Add existing property sheet) - Add `Falcor.vcxproj` to your solution (Located at `Framework/Source/Falcor.vcxproj`) -- Add a reference to Falcor in your project (Solution Explorer -> Right click your Project -> `Add` -> `Reference` -> Choose Falcor) +- Add a reference to Falcor in your project (Solution Explorer -> Your Project -> Right Click `References` -> Click `Add Reference...` -> Choose Falcor) *Sample* Class ------------------- @@ -70,8 +54,7 @@ Build Configurations Falcor has the following build configurations for DirectX 12, Vulkan and DXR, respectively: - `DebugD3D12` - `ReleaseD3D12` -- `DebugVK` -- `ReleaseVK` +- Currently, the `DebugVK` and `ReleaseVK` build is failing Debug builds enable file logging and message boxes by default, and there is a lot of runtime error checking. If debug layers for the selected API are installed, they will be loaded as well. @@ -123,11 +106,11 @@ If you use Falcor in a research project leading to a publication, please cite th The BibTex entry is ```bibtex -@Misc{Benty18, - author = {Nir Benty and Kai-Hwa Yao and Tim Foley and Matthew Oakes and Conor Lavelle and Chris Wyman}, +@Misc{Benty19, + author = {Nir Benty and Kai-Hwa Yao and Lucy Chen and Tim Foley and Matthew Oakes and Conor Lavelle and Chris Wyman}, title = {The {Falcor} Rendering Framework}, - year = {2018}, - month = {05}, + year = {2019}, + month = {10}, url = {https://github.com/NVIDIAGameWorks/Falcor}, note= {\url{https://github.com/NVIDIAGameWorks/Falcor}} } diff --git a/Samples/Core/ComputeShader/ComputeShader.cpp b/Samples/Core/ComputeShader/ComputeShader.cpp deleted file mode 100644 index 6c60d8f84..000000000 --- a/Samples/Core/ComputeShader/ComputeShader.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "ComputeShader.h" - -void ComputeShader::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (pGui->addButton("Load Image")) - { - loadImage(pSample); - } - pGui->addCheckBox("Pixelate", mbPixelate); -} - -Texture::SharedPtr createTmpTex(uint32_t width, uint32_t height) -{ - return Texture::create2D(width, height, ResourceFormat::RGBA8Unorm, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); -} - -void ComputeShader::onLoad(SampleCallbacks* pSample, RenderContext* pContext) -{ - mpProg = ComputeProgram::createFromFile("compute.hlsl", "main"); - mpState = ComputeState::create(); - mpState->setProgram(mpProg); - mpProgVars = ComputeVars::create(mpProg->getReflector()); - - Fbo::SharedPtr pFbo = pSample->getCurrentFbo(); - mpTmpTexture = createTmpTex(pFbo->getWidth(), pFbo->getHeight()); -} - -void ComputeShader::loadImage(SampleCallbacks* pSample) -{ - std::string filename; - FileDialogFilterVec filters = { {"bmp"}, {"jpg"}, {"dds"}, {"png"}, {"tiff"}, {"tif"}, {"tga"} }; - if(openFileDialog(filters, filename)) - { - loadImageFromFile(pSample, filename); - } -} - -void ComputeShader::loadImageFromFile(SampleCallbacks* pSample, std::string filename) -{ - mpImage = createTextureFromFile(filename, false, true); - mpProgVars->setTexture("gInput", mpImage); - mpTmpTexture = createTmpTex(mpImage->getWidth(), mpImage->getHeight()); - - pSample->resizeSwapChain(mpImage->getWidth(), mpImage->getHeight()); -} - -void ComputeShader::onFrameRender(SampleCallbacks* pSample, RenderContext* pContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - - if(mpImage) - { - pContext->clearUAV(mpTmpTexture->getUAV().get(), clearColor); - - if (mbPixelate) - { - mpProg->addDefine("_PIXELATE"); - } - else - { - mpProg->removeDefine("_PIXELATE"); - } - mpProgVars->setTexture("gOutput", mpTmpTexture); - - pContext->setComputeState(mpState); - pContext->setComputeVars(mpProgVars); - - uint32_t w = (mpImage->getWidth() / 16) + 1; - uint32_t h = (mpImage->getHeight() / 16) + 1; - pContext->dispatch(w, h, 1); - pContext->copyResource(pTargetFbo->getColorTexture(0).get(), mpTmpTexture.get()); - } - else - { - pContext->clearRtv(pTargetFbo->getRenderTargetView(0).get(), clearColor); - } -} - -void ComputeShader::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - if (mpTmpTexture) - { - mpTmpTexture = createTmpTex(width, height); - } -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - ComputeShader::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Compute Shader"; - config.windowDesc.resizableWindow = true; - config.deviceDesc.depthFormat = ResourceFormat::Unknown; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Core/ComputeShader/ComputeShader.vcxproj.filters b/Samples/Core/ComputeShader/ComputeShader.vcxproj.filters deleted file mode 100644 index 6b5ece448..000000000 --- a/Samples/Core/ComputeShader/ComputeShader.vcxproj.filters +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - {7b8ee002-6962-45ea-9ff9-419496e90c57} - - - - - Data - - - \ No newline at end of file diff --git a/Samples/Core/LearningWithEmbeddedPython/Data/RenderForLearning.slang b/Samples/Core/LearningWithEmbeddedPython/Data/RenderForLearning.slang deleted file mode 100644 index 830ec3e9e..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/Data/RenderForLearning.slang +++ /dev/null @@ -1,118 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -__import Shading; -__import Effects.CascadedShadowMap; -__import DefaultVS; - -layout(binding = 0) cbuffer PerFrameCB : register(b0) -{ - float3 gAmbient; - float gEnvMapFactorScale; - CsmData gCsmData; - float4x4 camVpAtLastCsmUpdate; - float2 gRenderTargetDim; - float gOpacityScale; -}; - -struct MainVsOut -{ - VertexOut vsData; - float shadowsDepthC : DEPTH; -}; - -Texture2D gEnvMap; -SamplerState gSampler; - -struct PsOut -{ - float4 color : SV_TARGET0; - float4 normal : SV_TARGET1; -#ifdef _OUTPUT_MOTION_VECTORS - float2 motion : SV_TARGET2; -#endif -}; - -MainVsOut vs(VertexIn vIn) -{ - MainVsOut vsOut; - vsOut.vsData = defaultVS(vIn); - -#ifdef _OUTPUT_MOTION_VECTORS - vsOut.vsData.prevPosH.xy += vsOut.vsData.prevPosH.w * 2 * float2(gCam.jitterX, gCam.jitterY); -#endif - - vsOut.shadowsDepthC = mul(float4(vsOut.vsData.posW, 1), camVpAtLastCsmUpdate).z; - return vsOut; -} - -PsOut ps(MainVsOut vOut, float4 pixelCrd : SV_POSITION) -{ - PsOut psOut; - - ShadingData shData = prepareShadingData(vOut.vsData, gMaterial, gCamera.posW); - - ShadingResult result; - float4 finalColor = float4(0, 0, 0, 1); - float envMapFactor = 1; - float opacity = 1; - - [unroll] - for (uint l = 0; l < _LIGHT_COUNT; l++) - { - float shadowFactor = 1; -#ifdef _ENABLE_SHADOWS - if (l == 0) - { - shadowFactor = calcShadowFactor(gCsmData, vOut.shadowsDepthC, shData.posW, vOut.vsData.posH.xy / vOut.vsData.posH.w); - shadowFactor *= shData.opacity; - envMapFactor -= 1 - shadowFactor; - } -#endif - finalColor.rgb += evalMaterial(shData, gLights[l], shadowFactor).color.rgb; - } - -#ifdef _ENABLE_TRANSPARENCY - finalColor.a = shData.opacity * gOpacityScale; -#endif - -#ifdef _ENABLE_REFLECTIONS - finalColor.rgb += evalMaterial(shData, gLightProbe).color.rgb; -#endif - - psOut.color = finalColor; - psOut.normal = float4(vOut.vsData.normalW * 0.5f + 0.5f, 1.0f); - -#ifdef _OUTPUT_MOTION_VECTORS - psOut.motion = calcMotionVector(pixelCrd.xy, vOut.vsData.prevPosH, gRenderTargetDim); -#endif - -#if defined(_VISUALIZE_CASCADES) && defined(_ENABLE_SHADOWS) - psOut.color.rgb *= getBlendedCascadeColor(gCsmData, vOut.shadowsDepthC); -#endif - return psOut; -} diff --git a/Samples/Core/LearningWithEmbeddedPython/Data/demo_infer.py b/Samples/Core/LearningWithEmbeddedPython/Data/demo_infer.py deleted file mode 100644 index 8bce8bea1..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/Data/demo_infer.py +++ /dev/null @@ -1,32 +0,0 @@ -#*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#************************************************************************** -npInferLight = np.ones((1, len(inferLight)), dtype = 'float32') -npInferLight[0] = inferLight -prediction = model.predict(npInferLight) -tmp = (prediction[0][0]*255.0).clip(0.,255.).astype(np.uint8) -infResult = tmp.reshape(512,512,4) diff --git a/Samples/Core/LearningWithEmbeddedPython/Data/demo_init.py b/Samples/Core/LearningWithEmbeddedPython/Data/demo_init.py deleted file mode 100644 index 6f5a9aff1..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/Data/demo_init.py +++ /dev/null @@ -1,85 +0,0 @@ -#*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#************************************************************************** -from tensorflow.contrib.keras.api.keras.layers import Input -from tensorflow.contrib.keras.api.keras.layers import UpSampling2D, UpSampling3D, Reshape -from tensorflow.contrib.keras.api.keras.models import Model, Sequential -from tensorflow.contrib.keras.api.keras.layers import Dense -from tensorflow.contrib.keras.api.keras.layers import Dropout -from tensorflow.contrib.keras.api.keras.layers import Flatten -from tensorflow.contrib.keras.api.keras.layers import Conv1D, Conv2D -from tensorflow.contrib.keras.api.keras.layers import MaxPooling2D -from tensorflow.contrib.keras.api.keras.optimizers import Adam -from tensorflow.contrib.keras.api.keras import backend as K -os.environ['TF_CPP_MIN_LOG_LEVEL']='2' -K.set_image_data_format('channels_last') -np.random.seed(7) - -def CreateSimpleImageModel_128(): - dataIn = Input(shape=(3,)) - layer = Dense(4 * 4, activation='tanh')(dataIn) - layer = Dense(128 * 128 * 4, activation='linear')(layer) - layer = Reshape((128, 128, 4))(layer) - layer = UpSampling3D((4, 4, 1))(layer) - layer = Reshape((1, 512, 512, 4))(layer) - modelOut = layer - model = Model(inputs=[dataIn], outputs=[modelOut]) - adam = Adam(lr=0.005, decay=0.0001) - model.compile(loss='mean_squared_error', optimizer=adam, metrics=['accuracy']) - return model - -def CreateSimpleImageModel_256(): - dataIn = Input(shape=(3,)) - layer = Dense(4 * 4, activation='tanh')(dataIn) - layer = Dense(256 * 256 * 4, activation='linear')(layer) - layer = Reshape((256, 256, 4))(layer) - layer = UpSampling3D((2, 2, 1))(layer) - layer = Reshape((1, 512, 512, 4))(layer) - modelOut = layer - model = Model(inputs=[dataIn], outputs=[modelOut]) - adam = Adam(lr=0.005, decay=0.0001) - model.compile(loss='mean_squared_error', optimizer=adam, metrics=['accuracy']) - return model - -def CreateSimpleImageModel_512(): - dataIn = Input(shape=(3,)) - layer = Dense(4 * 4, activation='tanh')(dataIn) - layer = Dense(512 * 512 * 4, activation='linear')(layer) - layer = Reshape((1, 512, 512, 4))(layer) - modelOut = layer - model = Model(inputs=[dataIn], outputs=[modelOut]) - adam = Adam(lr=0.005, decay=0.0001) - model.compile(loss='mean_squared_error', optimizer=adam, metrics=['accuracy']) - return model - -def ConvertDataToNumpy( trainDataIn, trainDataOut, resOutW, resOutH ): - npInput = np.ones((1, trainDataIn.shape[0]), dtype='float32') - npInput[0] = trainDataIn - tmpData = trainDataOut.reshape(resOutW,resOutH,4) / 256.0 - npOutput = np.zeros((1, 1, resOutW, resOutH,4), dtype='float32') - npOutput[0][0] = np.array(tmpData, dtype='float32') - return (npInput, npOutput) \ No newline at end of file diff --git a/Samples/Core/LearningWithEmbeddedPython/Data/demo_train.py b/Samples/Core/LearningWithEmbeddedPython/Data/demo_train.py deleted file mode 100644 index 3c3842470..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/Data/demo_train.py +++ /dev/null @@ -1,29 +0,0 @@ -#*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#************************************************************************** -modelIn, modelOut = ConvertDataToNumpy( lightData, imgData, imgW, imgH ) -model.fit(modelIn, modelOut, epochs=1) diff --git a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj b/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj deleted file mode 100644 index 8561fa6c2..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj +++ /dev/null @@ -1,123 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {B7D37434-A294-4E67-8420-AD09C54C10EF} - Win32Proj - LearningWithEmbeddedPython - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - $(FALCOR_PYBIND11_PATH)\include;$(FALCOR_PYTHON_PATH)\include;%(AdditionalIncludeDirectories) - - - Windows - true - $(FALCOR_PYTHON_PATH)\libs;%(AdditionalLibraryDirectories) - - - - - Level3 - - - MaxSpeed - true - true - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - $(FALCOR_PYBIND11_PATH)\include;$(FALCOR_PYTHON_PATH)\include;%(AdditionalIncludeDirectories) - - - Windows - true - true - true - $(FALCOR_PYTHON_PATH)\libs;%(AdditionalLibraryDirectories) - - - - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - - - - true - true - - - true - true - - - true - true - - - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj.filters b/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj.filters deleted file mode 100644 index b5fb54446..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.vcxproj.filters +++ /dev/null @@ -1,42 +0,0 @@ - - - - - {010261b4-e0a8-4953-b47e-d5d2f7f0f80c} - - - {a47a8b63-84be-49d1-99d3-cde96bff2662} - - - {d76ccbad-acaa-4b50-878b-f1c0cfe81ea1} - - - - - - Renderers - - - - - Renderers - - - - - - - - Python Snippets - - - Python Snippets - - - Python Snippets - - - Data - - - \ No newline at end of file diff --git a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython_overloadLoadScene.cpp b/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython_overloadLoadScene.cpp deleted file mode 100644 index a7a129ec2..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython_overloadLoadScene.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "MultiRendererSample.h" -#include "Renderers/ShaderToy.h" -#include "Renderers/LiveTrainingDemo.h" - -// We're need to override our scene loader to provide good default lights (if there are none), -// since our LiveTrainRenderer assumes there's (at least) one light in the scene. -class DemoApp : public MultiRendererSample -{ - virtual Scene::SharedPtr loadScene(const std::string& filename) override; -}; - - -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -{ - // Our application program - DemoApp sample; - - // Setup our demo config - SampleConfig config; - config.windowDesc.title = "Live Training & Python Integration Demo"; - config.windowDesc.resizableWindow = false; - config.windowDesc.width = 1800; - config.windowDesc.height = 980; - config.freezeTimeOnStartup = true; - - // Create our Python integration renderer -#if FALCOR_USE_PYTHON - Renderer::SharedPtr liveTrain = LiveTrainRenderer::create(); // The renderer that does Python-based live training (default; program starts showing this one) - sample.addRenderer(liveTrain); -#endif - - // Create another, fallback renderer to run if there's no Python enabled. - Renderer::SharedPtr toyDemo = ShaderToyRenderer::create(); // Another, super-simple renderer as an example of how to use the MultiRendererSample - sample.addRenderer( toyDemo ); - - // Run the program - sample.run( config ); -} - - - - -Scene::SharedPtr DemoApp::loadScene(const std::string& filename) -{ - Scene::SharedPtr scene = Scene::loadFromFile(filename); - if (scene != nullptr) - { - if (scene->getCameraCount() == 0) - { - const Model* pModel = scene->getModel(0).get(); - Camera::SharedPtr pCamera = Camera::create(); - vec3 position = pModel->getCenter(); - float radius = pModel->getRadius(); - position.y += 0.1f * radius; - scene->setCameraSpeed(radius * 0.03f); - pCamera->setPosition(position); - pCamera->setTarget(position + vec3(0, -0.3f, -radius)); - pCamera->setDepthRange(0.1f, radius * 10); - scene->addCamera(pCamera); - } - - if (scene->getLightCount() == 0) - { - DirectionalLight::SharedPtr pDirLight = DirectionalLight::create(); - pDirLight->setWorldDirection(vec3(-0.189f, -0.861f, -0.471f)); - pDirLight->setIntensity(vec3(1, 1, 0.985f) * 10.0f); - pDirLight->setName("DirLight"); - scene->addLight(pDirLight); - scene->setAmbientIntensity(vec3(0.1f)); - } - } - return scene; -} diff --git a/Samples/Core/LearningWithEmbeddedPython/README.txt b/Samples/Core/LearningWithEmbeddedPython/README.txt deleted file mode 100644 index 3a7130eaa..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/README.txt +++ /dev/null @@ -1,34 +0,0 @@ -This sample requires Python with TensorFlow installed, as it embeds a Python interpreter (by linking to Python librarires). -Since this is not a regular part of the Falcor distribution, using this sample (and Falcor's built-in PythonEmbedding class) -requires a bit more work than just building Falcor. - -There are 2 parts to this additional setup: - 1) Telling Falcor to compile with Python support - 2) Installing an appropriate Python tool chain - -First, tell Falcor to compile with Python support. - 1) Open FalcorConfig.h (see same directory as Falcor.h), and change the #define FALCOR_USE_PYTHON to be 1 - 2) In your OS, define two environment variables FALCOR_PYTHON_PATH and FALCOR_PYBIND11_PATH, depending on install locations. - * Remember to restart your Visual Studio so it grabs the updated environment variables. - * Ours paths were set: FALCOR_PYTHON_PATH=C:\Python\Python35 and FALCOR_PYBIND11_PATH=C:\Python\pybind11 - -Second, install an appropriate Python toolchain. - 1) Install Python. This needs to be a version compatible with TensorFlow. - * We grabbed directly from http://python.org - * We use Python 3.5.3 (any 3.5.x should work, but 3.6.x is not yet compatible with TensorFlow) - 2) In C++, we interface with Python (mostly) using the pybind11 library. - * This is really easy, since it's a header-only library. - * Grab that from here: https://github.com/pybind/pybind11 - 3) If you want GPU-accelerated TensorFlow, install CUDA and CUDNN and add them to your path. - * As of August 2017, CUDA 8 and CUDNN 5.1 (*not* 6.0) are the versions that work with TensorFlow - 4) Install TensorFlow. (pip3 install --upgrade tensorflow) - * We built against TensorFlow 1.2. Earlier versions *do not* currently work. - 5) Install SciPy. - * On Windows, you will probably need to grab from here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#scipy - * Ensure you grab the .whl for the right Python (i.e., scipy-0.19.0-cp35-cp35m-win_amd64.whl gets SciPy 0.19 for Python 3.5) - 6) Install NumPy+MKL. - * On Windows, you will probably need to grab from here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy - * Same Python versioning issue (i.e., we grabbed numpy-1.11.3+mkl-cp35-cp35m-win_amd64.whl) - 7) Install h5py (pip3 install h5py) - * You might be able to skip this step, as I think I removed h5py dependencies when prepping for the Falcor release. - 8) Install pillow (pip3 install pillow) diff --git a/Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.cpp b/Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.cpp deleted file mode 100644 index b02869849..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.cpp +++ /dev/null @@ -1,563 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#include "LiveTrainingDemo.h" - -#if FALCOR_USE_PYTHON - -#include "Python.h" -#include "pybind11/pybind11.h" -#include "pybind11/embed.h" -#include "pybind11/eval.h" -#include "pybind11/numpy.h" -#include "pybind11/stl.h" -#include "pybind11/buffer_info.h" -#include "Utils/PythonEmbedding.h" -#include -#include -#include -#include -#include - -// We use an arbitrarily brighten our light in this sample code. For no good reason except it looks better -// in the scenes we used. No good reason for this particular constant, either (except it arose empirically -// out of some, now irrelevant, experimental tests we did at one point). -#define LIGHT_INTENSITY 3.882f - -// Clarity. Cleans pybind11 call notation a bit. -namespace py = pybind11; - -void LiveTrainRenderer::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - // Don't re-initialize if we already have. - if (mIsInitialized) - { - return; - } - - // Seed our random number generator. - mRng.seed((uint32)time(0)); - - // Create a python interpreter with appropriate TensorFlow version imported - if (!mPython) - { - // Create our embedded Python interpreter, import some common modules - mPython = PythonEmbedding::create(); - mPython->importModule("os"); // think in Python: "import os" - mPython->importModule("io"); - mPython->importModule("sys"); - mPython->importModule("math"); - mPython->importModule("importlib"); - mPython->importModule("contextlib"); - mPython->importModule("numpy", "np"); // think in Python: "import numpy as np" - mPython->importModule("tensorflow", "tf"); - mPython->importModule("PIL"); - - // Get a copy of the Python dictionary that stores global variables - mGlobals = mPython->getGlobals(); - } - - // A texture to store the data we return from Python - mPythonReturnTexture = Texture::create2D( 512, 512, ResourceFormat::RGBA8UnormSrgb ); - - // Load out Python scripts - reloadPythonScripts(); - - // Run our Python-based initialization code - doPythonInit(); - - // Create a dropdown to swap between DNN sizes. - mDNNSizeList.push_back( { 0, "Learn 128 x 128 image" } ); - mDNNSizeList.push_back( { 1, "Learn 256 x 256 image" } ); - mDNNSizeList.push_back( { 2, "Learn 512 x 512 image" } ); - - mIsInitialized = true; -} - -void LiveTrainRenderer::doPythonInit() -{ - // Run our Python-based initialization code. If we fail, get error messages and set appropriate flags - if (!mPython->executeString(mPythonInit)) - { - mHasFailure = true; - mPythonInitialized = false; - mTestResult = mPython->getError(); - return; - } - - // Create our neural network model. This is separately encapsulated, because we call it a couple times - mPythonInitialized = ( executeStringAndSetFlags( mPythonCreateModel[mDNNSize] ) >= 0.0f ); -} - -void LiveTrainRenderer::doPythonTrain( Texture::SharedPtr fromTex ) -{ - // Extract a our image data from the DX context. - std::vector textureData = gpDevice->getRenderContext()->readTextureSubresource(fromTex.get(), fromTex->getSubresourceIndex(0, 0)); - - // Get our scene's light direction - Light* pLight = mpScene->getScene()->getLight(0).get(); - float dirData[3] = { pLight->getData().dirW.x, pLight->getData().dirW.y, pLight->getData().dirW.z }; - - // Actually pass our light direction to Python (used as the input for this training run on the network) - mGlobals["lightData"] = py::array_t({ 3 }, { 4 }, dirData); // data shape, data stride, raw data ptr (1D array of floats) - - // Pass Python information about our rendered image (used as the target/output for this training run on the network) - mGlobals["imgW"] = 512; - mGlobals["imgH"] = 512; - mGlobals["imgData"] = py::array_t( { 512, 512, 4 }, // Shape of the array - { 512 * 4 * sizeof(unsigned char), 4 * sizeof(unsigned char), sizeof(unsigned char) }, // data stride - textureData.data()); // raw buffer data (w*h*4 array of uchars) - - // If Python successfully transforms the input data into the NumPy arrays needed for the model training.... - mLastTrainTime = executeStringAndSetFlags( mPythonTrain ); - if (mLastTrainTime >= 0.0f) - { - mNumTrainingRuns++; - } - - mDoTraining = false; -} - -void LiveTrainRenderer::doPythonInference() -{ - // Send our light direction to Python - DirectionalLight *dirLight = (DirectionalLight *)(mpScene->getScene()->getLight(0).get()); - float dirData[3] = { dirLight->getData().dirW.x, dirLight->getData().dirW.y, dirLight->getData().dirW.z }; - mGlobals["inferLight"] = py::array_t( { 3 }, { 4 }, dirData ); - - // Predict the image given the above light direction - mLastInferenceTime = executeStringAndSetFlags(mPythonInfer); - if (mLastInferenceTime >= 0.0f) - { - // Readback the data. Cast our Python output to an uchar array - auto arr = mGlobals["infResult"].cast< py::array_t >(); - py::buffer_info arr_info = arr.request(); - - // Upload the Python uchar array into our texture (so we can render the result) - gpDevice->getRenderContext()->updateTextureSubresource(mPythonReturnTexture.get(), mPythonReturnTexture->getSubresourceIndex(0, 0), arr_info.ptr); - } - - mDoInference = false; -} - -void LiveTrainRenderer::doRandomTrain() -{ - // Select a random light direction - float phi = float(M_PI * randomFloat()); - float theta = float(2 * M_PI * randomFloat()); - vec3 rndDir = vec3(sinf(theta)*cosf(phi), sinf(theta)*sinf(phi), cosf(theta)); - - // Pass it it, along with our arbitrarily brightened light. - DirectionalLight *dirLight = (DirectionalLight *)(mpScene->getScene()->getLight(0).get()); - dirLight->setWorldDirection(rndDir); - dirLight->setIntensity(vec3(LIGHT_INTENSITY, LIGHT_INTENSITY, LIGHT_INTENSITY)); - - // Set flags to actually do training on next display refresh. - mDoTraining = mDoInference = true; - if (mTrainsLeft>0) mTrainsLeft--; -} - -void LiveTrainRenderer::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, Fbo::SharedPtr pTargetFbo) -{ - pRenderContext->clearFbo(pTargetFbo.get(), vec4(0.2f, 0.4f, 0.5f, 1), 1, 0); - - if (mpScene) - { - // Since, we're rendering into a differently sized window than our app window, reset aspect each frame, - // in case it get changed via a resize or other window message. - mpScene->getScene()->getActiveCamera()->setAspectRatio(1.0f); - - // Continuously training? Or set to train some batch of images? Do it. - if (mContinuousTrain || mTrainsLeft > 0) - { - doRandomTrain(); - } - - // Render our scene traditionally. - pRenderContext->getGraphicsState()->setFbo(mpMainFbo); - pRenderContext->clearFbo(mpMainFbo.get(), glm::vec4(), 1, 0, FboAttachmentType::All); - mpScene->update(pSample->getCurrentTime()); - lightingPass(pRenderContext); - msaaResolvePass(pRenderContext); - - // If training, convert the data to a format Python can look at. This is relatively slow & simplistic, - // since we could probably use the original FBO (mpResolveFbo) directly, but in this simple example, - // we just wanted to see if Python I/O transfers would work in simple cases. - if (mDoTraining) - { - pRenderContext->blit(mpResolveFbo->getColorTexture(0)->getSRV(), mpCaptureFbo->getRenderTargetView(0)); - doPythonTrain(mpCaptureFbo->getColorTexture(0)); - } - - // Use the current light direction to run inference from our current network. - if (mDoInference) - { - doPythonInference(); - } - - // Blit our rendering and our Python return textures onto the screen - pRenderContext->blit(mpResolveFbo->getColorTexture(0)->getSRV(), pTargetFbo->getRenderTargetView(0), uvec4(-1), uvec4(504, 284, 1016, 796)); - pRenderContext->blit(mPythonReturnTexture->getSRV(), pTargetFbo->getRenderTargetView(0), uvec4(-1), uvec4(1204, 284, 1716, 796)); - } -} - -void LiveTrainRenderer::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - // If we haven't loaded a scene, there's not much we can do. - if (!mpScene) - { - pGui->addText(mPythonInitialized ? "Python Initialized: (Successfully)" : "Python Initialized: **Failure**"); - pGui->addText(""); - pGui->addText("Load a scene to enable training..."); - if (pGui->addButton("Load Scene")) - { - std::string filename; - if (openFileDialog(Scene::kFileFormatString, filename)) - { - initNewScene(SceneRenderer::create(loadScene(filename))); - } - } - } - - // Now that we have a scene loaded, give more options. - else - { - // Allow the user to update the current light position - DirectionalLight *dirLight = (DirectionalLight *)(mpScene->getScene()->getLight(0).get()); - vec3 tmp = dirLight->getData().dirW; - if (pGui->addDirectionWidget("Direction", tmp)) - { - dirLight->setWorldDirection(tmp); - mDoInference = true; - } - - pGui->addSeparator(); - - if (pGui->beginGroup("Training & Inference", true)) - { - // Print some status messages - pGui->addText( mPythonInitialized ? "Python Initialized: (Successfully)" : "Python Initialized: **Failure**"); // Python available? - addTextHelper(pGui, "Trained example images: %d", int(mNumTrainingRuns)); // How many times trained? - addTextHelper(pGui, mLastTrainTime > 0 ? "Last train cost: %.3f ms" : "", mLastTrainTime ); // Last training cost (if available)? - addTextHelper(pGui, mLastInferenceTime > 0 ? "Last inference cost: %.3f ms" : "", mLastInferenceTime ); // Last inference cost (if available)? - pGui->addText(""); - - // Give some options for how to train... - if (pGui->addButton("(T) Train once")) - { - doRandomTrain(); - } - if (pGui->addButton("(B) Train batch of 128")) - { - mTrainsLeft = 128; - } - if (pGui->addButton("(C) Continuous Train")) - { - mContinuousTrain = !mContinuousTrain; - } - - pGui->addText(""); - pGui->addSeparator(); - - // Show some other options. These reset the training, so hide them in a closed group - if (pGui->beginGroup("Options (that reset training)")) - { - if (pGui->addDropdown("Network Size", mDNNSizeList, mDNNSize) || - pGui->addButton("(R) Reset DNN Model")) - { - mPythonInitialized = (executeStringAndSetFlags(mPythonCreateModel[mDNNSize]) >= 0.0f); - if (mPythonInitialized) - { - mDoInference = true; - mNumTrainingRuns = 0; - } - } - if (pGui->addButton("Reload Python Scripts")) - { - reloadPythonScripts(); // Reload Python - doPythonInit(); // Reinitialize all of Python - if (mPythonInitialized) // If that worked, again reset training state. - { - mDoInference = true; - mNumTrainingRuns = 0; - } - } - pGui->endGroup(); - } - - pGui->addSeparator(); - pGui->endGroup(); - } - } - - // If we get a Python error, print out Python's error in a window rather than silently failing. - if (mHasFailure) - { - pGui->pushWindow("Python Error Message:" , 1000, 250, 500, 10 ); - pGui->addText(mTestResult.c_str()); - pGui->popWindow(); - } -} - -bool LiveTrainRenderer::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - if (keyEvent.type != KeyboardEvent::Type::KeyPressed || !mpScene) - { - return false; - } - - switch (keyEvent.key) - { - case KeyboardEvent::Key::T: - doRandomTrain(); - break; - case KeyboardEvent::Key::C: - mContinuousTrain = !mContinuousTrain; - break; - case KeyboardEvent::Key::B: - mTrainsLeft = 128; - break; - case KeyboardEvent::Key::R: - // Recreate our model to reset it - mPythonInitialized = (executeStringAndSetFlags(mPythonCreateModel[mDNNSize]) >= 0.0f); - if (mPythonInitialized) - { - // Rerun inference, reset count of # of training runs - mDoInference = true; - mNumTrainingRuns = 0; - } - break; - default: - return false; - } - - return true; -} - -// Encapsulates calls to stringified Python code, plus timing the execution, setting any -// error flags, and grabbing an error message (if the Python code failed) -float LiveTrainRenderer::executeStringAndSetFlags(const std::string &pyCode) -{ - float curTime = -1.0f; - mHasFailure = false; - if (!mPython->executeString(pyCode)) - { - mTestResult = mPython->getError(); - mHasFailure = true; - } - else - { - curTime = float(mPython->lastExecutionTime()); - } - return curTime; -} - -// Stringify the on-disk Python scripts to avoid reading from disk each frame. -void LiveTrainRenderer::reloadPythonScripts( void ) -{ - // Load the initialization script - std::ifstream initFile("Data/demo_init.py"); - initFile.seekg(0, std::ios::end); - mPythonInit.reserve(initFile.tellg()); - initFile.seekg(0, std::ios::beg); - mPythonInit.assign((std::istreambuf_iterator(initFile)), std::istreambuf_iterator()); - - // Load the training script - std::ifstream trainFile("Data/demo_train.py"); - trainFile.seekg(0, std::ios::end); - mPythonTrain.reserve(trainFile.tellg()); - trainFile.seekg(0, std::ios::beg); - mPythonTrain.assign((std::istreambuf_iterator(trainFile)), std::istreambuf_iterator()); - - // Load the infer script - std::ifstream inferFile("Data/demo_infer.py"); - inferFile.seekg(0, std::ios::end); - mPythonInfer.reserve(inferFile.tellg()); - inferFile.seekg(0, std::ios::beg); - mPythonInfer.assign((std::istreambuf_iterator(inferFile)), std::istreambuf_iterator()); -} - -bool LiveTrainRenderer::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - // Prevents mouse motion from changing the camera. - if (mouseEvent.type == MouseEvent::Type::LeftButtonDown || - mouseEvent.type == MouseEvent::Type::LeftButtonUp) - { - return true; - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////////// -// Below here: Mostly encapsulation of simple/basic Falcor rendering code -/////////////////////////////////////////////////////////////////////////////////// - -void LiveTrainRenderer::msaaResolvePass(RenderContext* pContext) -{ - pContext->blit(mpMainFbo->getColorTexture(0)->getSRV(), mpResolveFbo->getRenderTargetView(0)); - pContext->blit(mpMainFbo->getColorTexture(1)->getSRV(), mpResolveFbo->getRenderTargetView(1)); - pContext->blit(mpMainFbo->getDepthStencilTexture()->getSRV(), mpResolveFbo->getRenderTargetView(2)); -} - -void LiveTrainRenderer::lightingPass(RenderContext* pContext) -{ - pContext->getGraphicsState()->setProgram(mLightingPass.pProgram); - pContext->setGraphicsVars(mLightingPass.pVars); - mpScene->renderScene(pContext.get()); -} - -void LiveTrainRenderer::initLightingPass() -{ - mLightingPass.pProgram = GraphicsProgram::createFromFile("RenderForLearning.slang", "vs", "ps"); - mLightingPass.pProgram->addDefine("_LIGHT_COUNT", std::to_string(mpScene->getScene()->getLightCount())); - initControls(); - mLightingPass.pVars = GraphicsVars::create(mLightingPass.pProgram->getReflector()); -} - -void LiveTrainRenderer::applyLightingProgramControl(ControlID controlId) -{ - const ProgramControl control = mControls[controlId]; - if (control.define.size()) - { - if (control.enabled) - { - mLightingPass.pProgram->addDefine(control.define, control.value); - } - else - { - mLightingPass.pProgram->removeDefine(control.define); - } - } -} - -void LiveTrainRenderer::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, ResourceFormat::RGBA8UnormSrgb); - mpCaptureFbo = FboHelper::create2D(512, 512, fboDesc); - - fboDesc.setColorTarget(0, ResourceFormat::RGBA32Float).setColorTarget(1, ResourceFormat::RGBA8Unorm).setColorTarget(2, ResourceFormat::R32Float); - mpResolveFbo = FboHelper::create2D(512, 512, fboDesc); - - fboDesc.setSampleCount(mSampleCount).setColorTarget(2, ResourceFormat::Unknown).setDepthStencilTarget(ResourceFormat::D32Float); - mpMainFbo = FboHelper::create2D(512, 512, fboDesc); -} - -void LiveTrainRenderer::initNewScene(SceneRenderer::SharedPtr pScene) -{ - mpScene = pScene; - - // Here's our arbitrary light intensity we're working with. - DirectionalLight *dirLight = (DirectionalLight *)(mpScene->getScene()->getLight(0).get()); - dirLight->setIntensity(vec3(LIGHT_INTENSITY, LIGHT_INTENSITY, LIGHT_INTENSITY)); - - initLightingPass(); -} - -void LiveTrainRenderer::initControls(void) -{ - mControls.resize(ControlID::Count); - mControls[ControlID::SuperSampling] = { false, "INTERPOLATION_MODE", "sample" }; - mControls[ControlID::EnableShadows] = { false, "_ENABLE_SHADOWS" }; - mControls[ControlID::EnableReflections] = { false, "_ENABLE_REFLECTIONS" }; - mControls[ControlID::EnableSSAO] = { false, "" }; - - for (uint32_t i = 0; i < ControlID::Count; i++) - { - applyLightingProgramControl((ControlID)i); - } -} - - -// Some dumb, printf-like helpers for adding text to the GUI. These were hacked in quickly to hide some of my UI mess before releasing the code. - -void LiveTrainRenderer::addTextHelper(Gui* pGui, const char *format, int intVal) -{ - char buf[512]; - if (format[0]) - { - sprintf_s(buf, format, intVal); - pGui->addText(buf); - } - else pGui->addText(""); -} - -void LiveTrainRenderer::addTextHelper(Gui* pGui, const char *format, float floatVal) -{ - char buf[512]; - if (format[0]) - { - sprintf_s(buf, format, floatVal); - pGui->addText(buf); - } - else pGui->addText(""); -} - -void LiveTrainRenderer::addTextHelper(Gui* pGui, const char *format, float floatVal1, float floatVal2) -{ - char buf[512]; - if (format[0]) - { - sprintf_s(buf, format, floatVal1, floatVal2); - pGui->addText(buf); - } - else pGui->addText(""); -} - -Scene::SharedPtr LiveTrainRenderer::loadScene(const std::string& filename) -{ - Scene::SharedPtr pScene = Scene::loadFromFile(filename); - if (pScene != nullptr) - { - if (pScene->getCameraCount() == 0) - { - const Model* pModel = pScene->getModel(0).get(); - Camera::SharedPtr pCamera = Camera::create(); - vec3 position = pModel->getCenter(); - float radius = pModel->getRadius(); - position.y += 0.1f * radius; - pScene->setCameraSpeed(radius * 0.03f); - pCamera->setPosition(position); - pCamera->setTarget(position + vec3(0, -0.3f, -radius)); - pCamera->setDepthRange(0.1f, radius * 10); - pScene->addCamera(pCamera); - } - - if (pScene->getLightCount() == 0) - { - DirectionalLight::SharedPtr pDirLight = DirectionalLight::create(); - pDirLight->setWorldDirection(vec3(-0.189f, -0.861f, -0.471f)); - pDirLight->setIntensity(vec3(1, 1, 0.985f) * 10.0f); - pDirLight->setName("DirLight"); - pScene->addLight(pDirLight); - } - } - return pScene; -} - -#endif diff --git a/Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.h b/Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.h deleted file mode 100644 index 7fae26372..000000000 --- a/Samples/Core/LearningWithEmbeddedPython/Renderers/LiveTrainingDemo.h +++ /dev/null @@ -1,176 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ - -#pragma once -#include "Falcor.h" - -#if FALCOR_USE_PYTHON - -#include "Utils/PythonEmbedding.h" -#include -#include - -using namespace Falcor; - -/** A class that implements a basic "live trained" deep neural network using Python + Tensorflow - to learn how to relight an image based on light direction. The training data is generated by - a raster engine live in front of the viewer. - */ -class LiveTrainRenderer : public Renderer, std::enable_shared_from_this -{ -public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - /** Constructor & destructors - */ - virtual ~LiveTrainRenderer() = default; - static SharedPtr create() { return std::make_shared(); } - - /** Callbacks overridden from Renderer base class - */ - virtual void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - virtual void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, Fbo::SharedPtr pTargetFbo) override; - virtual void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - virtual bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - virtual bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - virtual void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - - /** Methods to handle/encapsulate basic Falcor scene rendering - */ - void msaaResolvePass(RenderContext* context); - void lightingPass(RenderContext* context); - void initLightingPass(); - - /** Methods to encapsulate basic training/inference features - */ - void doPythonInit(); - void doPythonTrain(Texture::SharedPtr fromTex); - void doPythonInference(); - void doRandomTrain(); - - /** Encapsulates calls to stringified Python code, plus timing the execution, setting any - error flags, and grabbing an error message (if the Python code failed) - -> Returns the time (in ms) taken by execution. - -> If return value < 0, an execution error occurred. - */ - float executeStringAndSetFlags(const std::string &pyCode); - - /** Helper functions to make text additions to the GUI slightly cleaner in onGuiRender() - Probably should do these "right" at some point... - */ - void addTextHelper(Gui* pGui, const char *format, int intVal); - void addTextHelper(Gui* pGui, const char *format, float floatVal); - void addTextHelper(Gui* pGui, const char *format, float floatVal1, float floatVal2); - - /** Load / reload Python. Stringifies scripts to avoid reloading them each frame. - */ - void reloadPythonScripts(void); - -private: - Scene::SharedPtr loadScene(const std::string& filename); - void initNewScene(SceneRenderer::SharedPtr scene); - - // Stringified versions of our Python code. - std::string mPythonInit; - std::string mPythonTrain; - std::string mPythonInfer; - std::string mPythonCreateModel[3] = { - "model = CreateSimpleImageModel_128()", - "model = CreateSimpleImageModel_256()", - "model = CreateSimpleImageModel_512()" - }; - - // Dropdown & data to select between mPythonCreateModel[] variants - Gui::DropdownList mDNNSizeList; - uint32_t mDNNSize = 0; - - // Simplistic random number generation for randomizing light directions - std::mt19937 mRng; - float randomFloat() { return ((float)mRng()) / ((float)0xFFFFFFFFu); } - - // Our embedded Python interpreter - PythonEmbedding::SharedPtr mPython; - pybind11::dict mGlobals; - - int mTrainsLeft = 0; - - bool mHasFailure = false; - bool mIsInitialized = false; - bool mPythonInitialized = false; - - bool mDoTraining = false; - bool mDoInference = false; - char mSaveName[256]; - bool mContinuousTrain = false; - float mLastTrainTime = -1.0f; - float mLastInferenceTime = -1.0f; - std::string mTestResult = ""; - std::string mTrainCostStr = ""; - uint32_t mNumTrainingRuns = 0; - - Texture::SharedPtr mPythonReturnTexture = nullptr; - - /////////////////////////////////////////////////// - // Below here: Data for basic Falcor rendering - /////////////////////////////////////////////////// - SceneRenderer::SharedPtr mpScene = nullptr; - - struct - { - GraphicsVars::SharedPtr pVars; - GraphicsProgram::SharedPtr pProgram; - } mLightingPass; - - struct ProgramControl - { - bool enabled; - std::string define; - std::string value; - }; - enum ControlID - { - SuperSampling, - EnableShadows, - EnableReflections, - EnableSSAO, - - Count - }; - std::vector mControls; - - Fbo::SharedPtr mpMainFbo; - Fbo::SharedPtr mpResolveFbo; - Fbo::SharedPtr mpCaptureFbo; - uint32_t mSampleCount = 4; - - void applyLightingProgramControl(ControlID controlId); - void initControls(void); -}; - -#endif diff --git a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.cpp b/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.cpp deleted file mode 100644 index 1e14b5f2d..000000000 --- a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "MultiPassPostProcess.h" - -void MultiPassPostProcess::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (pGui->addButton("Load Image")) - { - loadImage(pSample); - } - pGui->addCheckBox("Gaussian Blur", mEnableGaussianBlur); - if(mEnableGaussianBlur) - { - mpGaussianBlur->renderUI(pGui, "Blur Settings"); - pGui->addCheckBox("Grayscale", mEnableGrayscale); - } -} - -void MultiPassPostProcess::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpLuminance = FullScreenPass::create("Luminance.ps.hlsl"); - mpGaussianBlur = GaussianBlur::create(5); - mpBlit = FullScreenPass::create("Blit.ps.hlsl"); - mpProgVars = GraphicsVars::create(mpBlit->getProgram()->getReflector()); -} - -void MultiPassPostProcess::loadImage(SampleCallbacks* pSample) -{ - std::string filename; - FileDialogFilterVec filters = { {"bmp"}, {"jpg"}, {"dds"}, {"png"}, {"tiff"}, {"tif"}, {"tga"} }; - if(openFileDialog(filters, filename)) - { - loadImageFromFile(pSample, filename); - } -} - -void MultiPassPostProcess::loadImageFromFile(SampleCallbacks* pSample, std::string filename) -{ - auto fboFormat = pSample->getCurrentFbo()->getColorTexture(0)->getFormat(); - mpImage = createTextureFromFile(filename, false, isSrgbFormat(fboFormat)); - - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, mpImage->getFormat()); - mpTempFB = FboHelper::create2D(mpImage->getWidth(), mpImage->getHeight(), fboDesc); - - pSample->resizeSwapChain(mpImage->getWidth(), mpImage->getHeight()); -} - -void MultiPassPostProcess::onFrameRender(SampleCallbacks* pSample, RenderContext* pContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pContext->clearFbo(pTargetFbo.get(), clearColor, 0, 0, FboAttachmentType::Color); - - if(mpImage) - { - // Grayscale is only with radial blur - mEnableGrayscale = mEnableGaussianBlur && mEnableGrayscale; - - pContext->setGraphicsVars(mpProgVars); - - if(mEnableGaussianBlur) - { - mpGaussianBlur->execute(pContext, mpImage, mpTempFB); - mpProgVars->setTexture("gTexture", mpTempFB->getColorTexture(0)); - const FullScreenPass* pFinalPass = mEnableGrayscale ? mpLuminance.get() : mpBlit.get(); - pFinalPass->execute(pContext); - } - else - { - mpProgVars->setTexture("gTexture", mpImage); - mpBlit->execute(pContext); - } - } -} - -void MultiPassPostProcess::onShutdown(SampleCallbacks* pSample) -{ -} - -bool MultiPassPostProcess::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - if (keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - switch (keyEvent.key) - { - case KeyboardEvent::Key::L: - loadImage(pSample); - return true; - case KeyboardEvent::Key::G: - mEnableGrayscale = true; - return true; - case KeyboardEvent::Key::B: - mEnableGaussianBlur = true; - return true; - } - } - return false; -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - MultiPassPostProcess::UniquePtr pRenderer = std::make_unique(); - - SampleConfig config; - config.windowDesc.title = "Multi-pass post-processing"; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj b/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj deleted file mode 100644 index 72884a07c..000000000 --- a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - - - - {282AAB9B-2150-447C-9C27-62C38C23761E} - Win32Proj - MultiPassPostProcess - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj.filters b/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj.filters deleted file mode 100644 index fe3fa64a4..000000000 --- a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - {e34e3747-1c5f-482c-b882-7b49f0910139} - - - - - Data - - - Data - - - \ No newline at end of file diff --git a/Samples/Core/ShaderBuffers/Data/teapot.obj b/Samples/Core/ShaderBuffers/Data/teapot.obj deleted file mode 100644 index 6fed4538c..000000000 --- a/Samples/Core/ShaderBuffers/Data/teapot.obj +++ /dev/null @@ -1,5049 +0,0 @@ -# Blender v2.65 (sub 0) OBJ File -# www.blender.org -o teapot.005 -v -0.498530 0.712498 -0.039883 -v -0.501666 0.699221 -0.063813 -v -0.501255 0.717792 0.000000 -v -0.624036 0.711938 -0.039883 -v -0.526706 0.651362 -0.039883 -v -0.508714 0.682112 -0.071712 -v -0.622039 0.698704 -0.063813 -v -0.624834 0.717232 0.000000 -v -0.498530 0.712498 0.039883 -v -0.638129 0.287158 0.000000 -v -0.517593 0.664661 -0.063813 -v -0.534329 0.646030 0.000000 -v -0.614850 0.651067 -0.039883 -v -0.616848 0.664299 -0.063813 -v -0.619445 0.681503 -0.071790 -v -0.741245 0.707456 -0.039883 -v -0.744483 0.712577 0.000000 -v -0.624036 0.711938 0.039883 -v -0.501667 0.699221 0.063813 -v -0.622039 0.698704 0.063813 -v -0.712095 0.661370 -0.063813 -v -0.733150 0.694655 -0.063813 -v -0.741245 0.707456 0.039883 -v -0.733150 0.694655 0.063813 -v -0.631184 0.277569 -0.039883 -v -0.526706 0.651362 0.039883 -v -0.614053 0.645774 0.000000 -v -0.704000 0.648569 -0.039883 -v -0.722621 0.678012 -0.071790 -v -0.832523 0.695296 -0.039883 -v -0.837545 0.699948 0.000000 -v -0.832523 0.695296 0.039883 -v -0.619445 0.681503 0.071790 -v -0.508714 0.682112 0.071712 -v -0.722621 0.678012 0.071790 -v -0.517593 0.664661 0.063813 -v -0.619922 0.238069 -0.071790 -v -0.624826 0.259599 -0.063813 -v -0.710066 0.328372 0.000000 -v -0.614850 0.651067 0.039883 -v -0.787321 0.653419 -0.063813 -v -0.803644 0.668539 -0.071790 -v -0.819967 0.683663 -0.063813 -v -0.819967 0.683663 0.063813 -v -0.803644 0.668539 0.071790 -v -0.711425 0.307332 -0.063813 -v -0.615553 0.216807 -0.063813 -v -0.712688 0.287795 -0.071790 -v -0.631184 0.277569 0.039883 -v -0.710455 0.322361 -0.039883 -v -0.710455 0.322361 0.039883 -v -0.700762 0.643448 0.000000 -v -0.774766 0.641786 -0.039883 -v -0.897800 0.671612 -0.039883 -v -0.904015 0.675354 0.000000 -v -0.897800 0.671612 0.039883 -v -0.882265 0.662257 0.063813 -v -0.712095 0.661370 0.063813 -v -0.787321 0.653419 0.063813 -v -0.608884 0.198682 -0.039883 -v -0.624828 0.259599 0.063813 -v -0.766936 0.377559 0.000000 -v -0.769651 0.372307 0.039883 -v -0.616848 0.664299 0.063813 -v -0.704000 0.648569 0.039883 -v -0.841868 0.637931 -0.063813 -v -0.862065 0.650094 -0.071790 -v -0.882265 0.662257 -0.063813 -v -0.862065 0.650094 0.071790 -v -0.841868 0.637931 0.063813 -v -0.611709 0.194244 0.000000 -v -0.776434 0.359177 -0.063813 -v -0.769651 0.372307 -0.039883 -v -0.713952 0.268259 -0.063813 -v -0.711425 0.307332 0.063813 -v -0.776434 0.359177 0.063813 -v -0.769743 0.637131 0.000000 -v -0.826329 0.628576 -0.039883 -v -0.937016 0.632565 -0.039883 -v -0.943899 0.634805 0.000000 -v -0.937016 0.632565 0.039883 -v -0.919812 0.626965 0.063813 -v -0.897443 0.619684 0.071790 -v -0.774766 0.641786 0.039883 -v -0.826329 0.628576 0.039883 -v -0.714922 0.253231 -0.039883 -v -0.608883 0.198681 0.039883 -v -0.715311 0.247220 0.000000 -v -0.785253 0.342107 -0.071790 -v -0.619922 0.238069 0.071790 -v -0.712688 0.287795 0.071790 -v -0.809626 0.430737 0.000000 -v -0.814205 0.426194 0.039883 -v -0.825653 0.414838 0.063813 -v -0.875076 0.612403 -0.063813 -v -0.897443 0.619684 -0.071790 -v -0.919812 0.626965 -0.063813 -v -0.875076 0.612403 0.063813 -v -0.857869 0.606800 0.039883 -v -0.794072 0.325038 -0.063813 -v -0.800855 0.311909 -0.039883 -v -0.825653 0.414838 -0.063813 -v -0.814205 0.426194 -0.039883 -v -0.615480 0.216617 0.063578 -v -0.785253 0.342107 0.071790 -v -0.840534 0.400078 0.071790 -v -0.820114 0.624834 0.000000 -v -0.857869 0.606800 -0.039883 -v -0.950104 0.574316 -0.039883 -v -0.957194 0.574316 0.000000 -v -0.950104 0.574316 0.039883 -v -0.932377 0.574316 0.063813 -v -0.909334 0.574316 0.071790 -v -0.886292 0.574316 0.063813 -v -0.850987 0.604560 0.000000 -v -0.714922 0.253231 0.039883 -v -0.803571 0.306656 0.000000 -v -0.840534 0.400078 -0.071790 -v -0.713952 0.268259 0.063813 -v -0.794072 0.325038 0.063813 -v -0.839022 0.483916 0.000000 -v -0.844976 0.480304 0.039883 -v -0.859854 0.471278 0.063813 -v -0.879202 0.459542 0.071790 -v -0.886292 0.574316 -0.063813 -v -0.909334 0.574316 -0.071790 -v -0.932377 0.574316 -0.063813 -v -0.868564 0.574316 0.039883 -v -0.861474 0.574316 0.000000 -v -0.855419 0.385315 -0.063813 -v -0.866867 0.373960 -0.039883 -v -0.859854 0.471278 -0.063813 -v -0.844976 0.480304 -0.039883 -v -0.855419 0.385315 0.063813 -v -0.898547 0.447807 0.063813 -v -0.868564 0.574316 -0.039883 -v -0.941014 0.505765 -0.039883 -v -0.947813 0.503580 0.000000 -v -0.941014 0.505765 0.039883 -v -0.924011 0.511234 0.063813 -v -0.901913 0.518343 0.071790 -v -0.879811 0.525448 0.063813 -v -0.862808 0.530917 0.039883 -v -0.800855 0.311909 0.039883 -v -0.871445 0.369416 0.000000 -v -0.879202 0.459542 -0.071790 -v -0.866867 0.373960 0.039883 -v -0.856009 0.533103 0.000000 -v -0.879811 0.525448 -0.063813 -v -0.901913 0.518343 -0.071790 -v -0.924011 0.511234 -0.063813 -v -0.862808 0.530917 -0.039883 -v -0.898547 0.447807 -0.063813 -v -0.913428 0.438781 -0.039883 -v -0.913428 0.438781 0.039883 -v -0.919378 0.435169 0.000000 -v 0.600960 0.444810 0.085753 -v 0.605956 0.463769 0.000000 -v 0.600959 0.444810 -0.085753 -v 0.656890 0.471064 0.000000 -v 0.661223 0.454734 -0.083705 -v 0.730696 0.501576 -0.073611 -v 0.661223 0.454734 0.083705 -v 0.605101 0.399712 -0.137265 -v 0.746455 0.470391 -0.117778 -v 0.724395 0.514048 0.000000 -v 0.605100 0.399712 0.137265 -v 0.672055 0.413907 -0.133928 -v 0.613258 0.341675 -0.154354 -v 0.786583 0.544847 -0.096783 -v 0.768856 0.565896 -0.060489 -v 0.672055 0.413907 0.133928 -v 0.730696 0.501576 0.073611 -v 0.686135 0.360830 -0.150669 -v 0.809626 0.517481 -0.108881 -v 0.766935 0.429850 -0.132501 -v 0.761767 0.574316 0.000000 -v 0.613258 0.341675 0.154354 -v 0.813417 0.626247 -0.075788 -v 0.839021 0.611098 -0.085261 -v 0.793721 0.637899 -0.047367 -v 0.686135 0.360830 0.150669 -v 0.768856 0.565896 0.060489 -v 0.746455 0.470391 0.117778 -v 0.619427 0.283145 -0.137236 -v 0.864627 0.595949 -0.075788 -v 0.832669 0.490118 -0.096783 -v 0.787419 0.389310 -0.117778 -v 0.785843 0.642561 0.000000 -v 0.619427 0.283145 0.137236 -v 0.700219 0.307756 -0.133928 -v 0.847933 0.703560 -0.059638 -v 0.879938 0.698065 -0.067092 -v 0.911944 0.692571 -0.059638 -v 0.823314 0.707784 -0.037273 -v 0.766935 0.429850 0.132501 -v 0.793721 0.637899 0.047367 -v 0.786583 0.544847 0.096783 -v 0.700219 0.307756 0.133928 -v 0.617684 0.235930 -0.085941 -v 0.936563 0.688344 -0.037273 -v 0.884319 0.584297 -0.047367 -v 0.850396 0.469070 -0.060489 -v 0.803175 0.358128 -0.073611 -v 0.813468 0.709475 0.000000 -v 0.617684 0.235930 0.085941 -v 0.625577 0.219883 0.000000 -v 0.711051 0.266929 -0.083705 -v 0.911107 0.765755 -0.053178 -v 0.957193 0.765755 -0.059825 -v 1.003279 0.765755 -0.053178 -v 1.038733 0.765755 -0.033236 -v 0.875654 0.765755 -0.033236 -v 0.809626 0.517481 0.108881 -v 0.787419 0.389310 0.117778 -v 0.823314 0.707784 0.037273 -v 0.813417 0.626247 0.075788 -v 0.711051 0.266929 0.083705 -v 0.715384 0.250599 0.000000 -v 1.052913 0.765755 0.000000 -v 0.946409 0.686653 0.000000 -v 0.892200 0.579635 0.000000 -v 0.857486 0.460650 0.000000 -v 0.809479 0.345652 0.000000 -v 0.861474 0.765755 0.000000 -v 0.929990 0.776479 -0.051602 -v 0.979075 0.777181 -0.058052 -v 1.028157 0.777879 -0.051602 -v 1.065915 0.778419 -0.032251 -v 1.081016 0.778632 0.000000 -v 0.892235 0.775943 -0.032251 -v 0.839021 0.611098 0.085261 -v 0.832669 0.490118 0.096783 -v 0.803175 0.358128 0.073611 -v 0.875654 0.765755 0.033236 -v 0.847933 0.703560 0.059638 -v 1.065915 0.778419 0.032174 -v 1.038733 0.765755 0.033236 -v 0.936563 0.688344 0.037273 -v 0.884319 0.584297 0.047367 -v 0.850396 0.469070 0.060489 -v 0.877131 0.775726 0.000000 -v 0.943713 0.783087 -0.047663 -v 0.992645 0.784366 -0.053621 -v 1.041577 0.785649 -0.047663 -v 1.079216 0.786631 -0.029789 -v 1.094273 0.787027 0.000000 -v 1.079216 0.786631 0.029174 -v 0.906073 0.782101 -0.029789 -v 0.879938 0.698065 0.067092 -v 0.864627 0.595949 0.075788 -v 0.892235 0.775943 0.032236 -v 0.911107 0.765755 0.053178 -v 1.041577 0.785649 0.046875 -v 1.028157 0.777879 0.051503 -v 1.003279 0.765755 0.053178 -v 0.911944 0.692571 0.059638 -v 0.891016 0.781708 0.000000 -v 0.951249 0.785448 -0.042542 -v 0.997575 0.787068 -0.047860 -v 1.043903 0.788686 -0.042542 -v 1.079539 0.789934 -0.026589 -v 1.093795 0.790431 0.000000 -v 1.079539 0.789934 0.024511 -v 1.043903 0.788686 0.039883 -v 0.915613 0.784200 -0.026589 -v 0.957193 0.765755 0.059825 -v 0.906073 0.782101 0.029666 -v 0.929990 0.776479 0.051553 -v 0.997575 0.787068 0.045616 -v 0.992645 0.784366 0.052956 -v 0.979075 0.777181 0.057969 -v 0.901357 0.783702 0.000000 -v 0.951569 0.783431 -0.037421 -v 0.993532 0.785033 -0.042099 -v 1.035492 0.786631 -0.037421 -v 1.067772 0.787863 -0.023388 -v 1.080684 0.788354 0.000000 -v 1.067772 0.787863 0.018464 -v 1.035492 0.786631 0.031119 -v 0.993532 0.785033 0.036781 -v 0.919292 0.782200 -0.023388 -v 0.915613 0.784200 0.026173 -v 0.943713 0.783087 0.047269 -v 0.951569 0.783431 0.034270 -v 0.951249 0.785448 0.041213 -v 0.906379 0.781708 0.000000 -v 0.943653 0.776909 -0.033482 -v 0.980182 0.778010 -0.037667 -v 1.016712 0.779111 -0.033482 -v 1.044812 0.779957 -0.020926 -v 1.056052 0.780295 0.000000 -v 1.044812 0.779957 0.011310 -v 1.016712 0.779111 0.021172 -v 0.980182 0.778010 0.027281 -v 0.943653 0.776909 0.027327 -v 0.915553 0.776064 -0.020926 -v 0.919292 0.782200 0.022403 -v 0.915553 0.776064 0.019003 -v 0.904312 0.775726 0.000000 -v 0.926468 0.765755 -0.031906 -v 0.957193 0.765755 -0.035895 -v 0.987920 0.765755 -0.031906 -v 1.011552 0.765755 -0.019942 -v 1.021006 0.765755 0.000000 -v 1.011552 0.765755 0.003324 -v 0.987920 0.765755 0.010635 -v 0.957193 0.765755 0.017947 -v 0.926468 0.765755 0.021271 -v 0.902834 0.765755 0.016618 -v 0.902834 0.765755 -0.019942 -v 0.893380 0.765755 0.000000 -v 0.886428 0.750924 -0.019014 -v 0.908324 0.750924 -0.030099 -v 0.936793 0.750924 -0.033795 -v 0.965261 0.750924 -0.030099 -v 0.987158 0.750924 -0.019014 -v 0.995918 0.750924 -0.000537 -v 0.987158 0.750924 0.002542 -v 0.965261 0.750924 0.009317 -v 0.936793 0.750924 0.016092 -v 0.908324 0.750924 0.019171 -v 0.886428 0.750924 0.014860 -v 0.877668 0.750924 -0.000537 -v 0.936793 0.750924 -0.007312 -v 0.440746 0.783205 0.000000 -v 0.446690 0.765755 0.000000 -v 0.430973 0.765755 0.119945 -v 0.425236 0.783205 0.118348 -v 0.425236 0.783205 -0.118348 -v 0.453011 0.750009 0.000000 -v 0.437073 0.750009 0.121642 -v 0.441668 0.793673 0.000000 -v 0.386470 0.765755 0.226985 -v 0.430973 0.765755 -0.119945 -v 0.426127 0.793673 -0.118596 -v 0.437073 0.750009 -0.121642 -v 0.426127 0.793673 0.118596 -v 0.381327 0.783205 0.223964 -v 0.381327 0.783205 -0.223964 -v 0.382124 0.793673 -0.224433 -v 0.317150 0.765755 0.317150 -v 0.391939 0.750009 0.230197 -v 0.321638 0.750009 0.321639 -v 0.386470 0.765755 -0.226985 -v 0.391939 0.750009 -0.230197 -v 0.447686 0.797164 0.000000 -v 0.431936 0.797164 -0.120212 -v 0.387332 0.797164 -0.227491 -v 0.230197 0.750009 0.391940 -v 0.226984 0.765755 0.386470 -v 0.317150 0.765755 -0.317150 -v 0.321638 0.750009 -0.321639 -v 0.431936 0.797164 0.120212 -v 0.382124 0.793673 0.224433 -v 0.312929 0.783205 0.312929 -v 0.313584 0.793673 -0.313584 -v 0.312929 0.783205 -0.312929 -v 0.317858 0.797164 -0.317858 -v 0.121642 0.750009 0.437072 -v 0.119944 0.765755 0.430973 -v 0.226984 0.765755 -0.386470 -v 0.230197 0.750009 -0.391940 -v 0.457031 0.793673 0.000000 -v 0.440950 0.793673 -0.122721 -v 0.395416 0.793673 -0.232239 -v 0.324491 0.793673 -0.324492 -v -0.000000 0.750009 0.453012 -v -0.000000 0.765755 0.446690 -v 0.223963 0.783205 0.381327 -v 0.223963 0.783205 -0.381327 -v 0.119944 0.765755 -0.430973 -v 0.121642 0.750009 -0.437072 -v 0.440950 0.793673 0.122721 -v 0.387332 0.797164 0.227491 -v 0.313584 0.793673 0.313584 -v 0.227491 0.797164 -0.387332 -v 0.224433 0.793673 -0.382125 -v 0.232239 0.793673 -0.395417 -v -0.119945 0.765755 0.430973 -v -0.121642 0.750009 0.437072 -v 0.118348 0.783205 0.425237 -v 0.118348 0.783205 -0.425237 -v -0.000000 0.750009 -0.453012 -v -0.000000 0.765755 -0.446690 -v 0.467924 0.783205 0.000000 -v 0.451460 0.783205 -0.125646 -v 0.404842 0.783205 -0.237775 -v 0.332226 0.783205 -0.332226 -v 0.237775 0.783205 -0.404842 -v -0.226985 0.765755 0.386470 -v -0.000000 0.783205 0.440746 -v 0.224433 0.793673 0.382125 -v 0.118596 0.793673 -0.426127 -v -0.000000 0.783205 -0.440746 -v -0.119945 0.765755 -0.430973 -v -0.121642 0.750009 -0.437072 -v 0.451460 0.783205 0.125646 -v 0.395416 0.793673 0.232239 -v 0.317858 0.797164 0.317858 -v 0.122721 0.793673 -0.440950 -v 0.120212 0.797164 -0.431937 -v 0.125646 0.783205 -0.451460 -v -0.317150 0.765755 0.317150 -v -0.230198 0.750009 0.391939 -v -0.321639 0.750009 0.321639 -v -0.118348 0.783205 0.425237 -v 0.118596 0.793673 0.426127 -v -0.000000 0.793673 -0.441668 -v -0.118348 0.783205 -0.425237 -v -0.226985 0.765755 -0.386470 -v 0.478596 0.765755 0.000000 -v 0.461756 0.765755 -0.128512 -v 0.414076 0.765755 -0.243198 -v 0.339803 0.765755 -0.339804 -v 0.243198 0.765755 -0.414076 -v 0.128512 0.765755 -0.461757 -v -0.391940 0.750009 0.230197 -v -0.386470 0.765755 0.226985 -v -0.223964 0.783205 0.381327 -v -0.000000 0.793673 0.441668 -v 0.227491 0.797164 0.387332 -v -0.000000 0.797164 -0.447686 -v -0.118596 0.793673 -0.426127 -v -0.223964 0.783205 -0.381327 -v -0.317150 0.765755 -0.317150 -v -0.230198 0.750009 -0.391939 -v -0.321639 0.750009 -0.321639 -v 0.461756 0.765755 0.128512 -v 0.404842 0.783205 0.237775 -v 0.324491 0.793673 0.324492 -v -0.000000 0.783205 -0.467924 -v -0.000000 0.793673 -0.457031 -v -0.000000 0.765755 -0.478597 -v -0.437073 0.750009 0.121642 -v -0.430974 0.765755 0.119945 -v -0.312929 0.783205 0.312929 -v -0.118596 0.793673 0.426127 -v 0.120212 0.797164 0.431937 -v -0.120212 0.797164 -0.431937 -v -0.224433 0.793673 -0.382125 -v -0.312929 0.783205 -0.312929 -v -0.386470 0.765755 -0.226985 -v -0.391940 0.750009 -0.230197 -v 0.518110 0.682112 0.000000 -v 0.499881 0.682112 -0.139122 -v 0.448260 0.682112 -0.263277 -v 0.367859 0.682112 -0.367859 -v 0.263277 0.682112 -0.448260 -v 0.139122 0.682112 -0.499882 -v -0.000000 0.682112 -0.518110 -v -0.453012 0.750009 0.000000 -v -0.446690 0.765755 0.000000 -v -0.381327 0.783205 0.223964 -v -0.224433 0.793673 0.382125 -v -0.000000 0.797164 0.447686 -v 0.232239 0.793673 0.395417 -v -0.122721 0.793673 -0.440950 -v -0.227491 0.797164 -0.387332 -v -0.313584 0.793673 -0.313584 -v -0.381327 0.783205 -0.223964 -v -0.430974 0.765755 -0.119945 -v 0.499881 0.682112 0.139122 -v 0.414076 0.765755 0.243198 -v 0.332226 0.783205 0.332226 -v -0.128513 0.765755 -0.461757 -v -0.125646 0.783205 -0.451460 -v -0.139123 0.682112 -0.499882 -v -0.437073 0.750009 -0.121642 -v -0.425237 0.783205 0.118348 -v -0.313584 0.793673 0.313584 -v -0.120212 0.797164 0.431937 -v 0.122721 0.793673 0.440950 -v -0.232240 0.793673 -0.395417 -v -0.317859 0.797164 -0.317858 -v -0.382125 0.793673 -0.224433 -v -0.425237 0.783205 -0.118348 -v 0.555408 0.599133 0.000000 -v 0.535865 0.599133 -0.149137 -v 0.480530 0.599133 -0.282230 -v 0.394341 0.599133 -0.394341 -v 0.282230 0.599133 -0.480530 -v 0.149137 0.599133 -0.535866 -v -0.000000 0.599133 -0.555408 -v -0.149138 0.599133 -0.535866 -v -0.440746 0.783205 0.000000 -v -0.382125 0.793673 0.224433 -v -0.227491 0.797164 0.387332 -v -0.000000 0.793673 0.457031 -v 0.237775 0.783205 0.404842 -v -0.237775 0.783205 -0.404842 -v -0.324492 0.793673 -0.324492 -v -0.387332 0.797164 -0.227491 -v -0.426127 0.793673 -0.118596 -v 0.535865 0.599133 0.149137 -v 0.448260 0.682112 0.263277 -v 0.339803 0.765755 0.339804 -v -0.263278 0.682112 -0.448260 -v -0.243198 0.765755 -0.414076 -v -0.282230 0.599133 -0.480530 -v -0.426127 0.793673 0.118596 -v -0.317859 0.797164 0.317858 -v -0.122721 0.793673 0.440950 -v 0.125646 0.783205 0.451460 -v -0.332226 0.783205 -0.332226 -v -0.395417 0.793673 -0.232239 -v -0.431937 0.797164 -0.120212 -v -0.441668 0.793673 0.000000 -v 0.588275 0.517481 0.000000 -v 0.567578 0.517481 -0.157963 -v 0.508969 0.517485 -0.298931 -v 0.417675 0.517481 -0.417675 -v 0.298931 0.517485 -0.508969 -v 0.157963 0.517485 -0.567578 -v -0.000000 0.517481 -0.588275 -v -0.157963 0.517481 -0.567578 -v -0.298931 0.517485 -0.508969 -v -0.387332 0.797164 0.227491 -v -0.232240 0.793673 0.395417 -v -0.000000 0.783205 0.467924 -v 0.243198 0.765755 0.414076 -v -0.339804 0.765755 -0.339804 -v -0.404842 0.783205 -0.237775 -v -0.440950 0.793673 -0.122721 -v -0.447686 0.797164 0.000000 -v 0.567578 0.517485 0.157963 -v 0.480530 0.599133 0.282230 -v 0.367859 0.682112 0.367859 -v -0.394341 0.599133 -0.394341 -v -0.367859 0.682112 -0.367859 -v -0.417675 0.517481 -0.417675 -v -0.431937 0.797164 0.120212 -v -0.324492 0.793673 0.324492 -v -0.125646 0.783205 0.451460 -v 0.128512 0.765755 0.461757 -v -0.414076 0.765755 -0.243198 -v -0.451461 0.783205 -0.125646 -v -0.457031 0.793673 0.000000 -v 0.592873 0.437827 -0.165003 -v 0.531651 0.437827 -0.312254 -v 0.436292 0.437827 -0.436292 -v 0.312254 0.437827 -0.531651 -v 0.165003 0.437827 -0.592873 -v -0.000000 0.437827 -0.614496 -v -0.165004 0.437827 -0.592873 -v -0.312255 0.437827 -0.531651 -v -0.436292 0.437827 -0.436292 -v -0.395417 0.793673 0.232239 -v -0.237775 0.783205 0.404842 -v -0.000000 0.765755 0.478597 -v 0.263277 0.682112 0.448260 -v -0.448260 0.682112 -0.263277 -v -0.461757 0.765755 -0.128512 -v -0.467924 0.783205 0.000000 -v -0.440950 0.793673 0.122721 -v 0.592873 0.437827 0.165003 -v 0.508969 0.517485 0.298931 -v 0.394341 0.599133 0.394341 -v -0.508969 0.517485 -0.298931 -v -0.480530 0.599133 -0.282230 -v -0.531651 0.437827 -0.312254 -v -0.332226 0.783205 0.332226 -v -0.128513 0.765755 0.461757 -v 0.139122 0.682112 0.499882 -v -0.499882 0.682112 -0.139122 -v -0.478597 0.765755 0.000000 -v -0.451461 0.783205 0.125646 -v 0.546669 0.360830 -0.321075 -v 0.448614 0.360830 -0.448614 -v 0.321074 0.360830 -0.546669 -v 0.169664 0.360830 -0.609621 -v -0.000000 0.360830 -0.631850 -v -0.169664 0.360830 -0.609621 -v -0.321075 0.360830 -0.546669 -v -0.448615 0.360830 -0.448614 -v -0.546669 0.360830 -0.321075 -v -0.404842 0.783205 0.237775 -v -0.243198 0.765755 0.414076 -v -0.000000 0.682112 0.518110 -v 0.282230 0.599133 0.480530 -v -0.535866 0.599133 -0.149137 -v -0.461757 0.765755 0.128512 -v 0.531651 0.437827 0.312254 -v 0.417675 0.517481 0.417675 -v 0.609621 0.360830 -0.169664 -v -0.592873 0.437827 -0.165003 -v -0.567578 0.517485 -0.157963 -v -0.609621 0.360830 -0.169664 -v -0.339804 0.765755 0.339804 -v -0.139123 0.682112 0.499882 -v 0.149137 0.599133 0.535866 -v -0.555408 0.599133 0.000000 -v -0.499882 0.682112 0.139122 -v -0.414076 0.765755 0.243198 -v 0.609621 0.360830 0.169664 -v 0.552100 0.287158 -0.324265 -v 0.453072 0.287158 -0.453072 -v 0.324265 0.287158 -0.552100 -v 0.171349 0.287158 -0.615677 -v -0.000000 0.287158 -0.638129 -v -0.171350 0.287158 -0.615677 -v -0.324265 0.287158 -0.552100 -v -0.453072 0.287158 -0.453072 -v -0.552100 0.287158 -0.324265 -v -0.615677 0.287158 -0.171349 -v -0.263278 0.682112 0.448260 -v -0.000000 0.599133 0.555408 -v 0.298931 0.517485 0.508969 -v -0.588275 0.517481 0.000000 -v -0.448260 0.682112 0.263277 -v 0.546669 0.360830 0.321075 -v 0.436292 0.437827 0.436292 -v 0.615677 0.287158 -0.171349 -v -0.631850 0.360830 0.000000 -v -0.614496 0.437827 0.000000 -v -0.367859 0.682112 0.367859 -v -0.149138 0.599133 0.535866 -v 0.157963 0.517481 0.567578 -v -0.567578 0.517481 0.157963 -v -0.480530 0.599133 0.282230 -v 0.615677 0.287158 0.171349 -v 0.541877 0.221240 -0.318259 -v 0.444680 0.221240 -0.444680 -v 0.318259 0.221240 -0.541877 -v 0.168176 0.221240 -0.604276 -v -0.000000 0.221240 -0.626311 -v -0.168177 0.221240 -0.604276 -v -0.318259 0.221240 -0.541877 -v -0.444680 0.221240 -0.444680 -v -0.541877 0.221240 -0.318259 -v -0.604277 0.221240 -0.168176 -v -0.282230 0.599133 0.480530 -v -0.000000 0.517481 0.588275 -v 0.312254 0.437827 0.531651 -v -0.592873 0.437827 0.165003 -v -0.535866 0.599133 0.149137 -v -0.394341 0.599133 0.394341 -v 0.552100 0.287158 0.324265 -v 0.448614 0.360830 0.448614 -v 0.604276 0.221240 -0.168176 -v -0.615677 0.287158 0.171349 -v -0.609621 0.360830 0.169664 -v -0.157963 0.517485 0.567578 -v 0.165003 0.437827 0.592873 -v -0.531651 0.437827 0.312254 -v -0.508969 0.517485 0.298931 -v -0.417675 0.517481 0.417675 -v 0.604276 0.221240 0.168176 -v 0.516317 0.166623 -0.303247 -v 0.423705 0.166623 -0.423705 -v 0.303247 0.166623 -0.516317 -v 0.160243 0.166623 -0.575771 -v -0.000000 0.166623 -0.596769 -v -0.160244 0.166623 -0.575771 -v -0.303247 0.166623 -0.516317 -v -0.423705 0.166623 -0.423705 -v -0.516317 0.166623 -0.303247 -v -0.575771 0.166623 -0.160243 -v -0.298931 0.517485 0.508969 -v -0.000000 0.437827 0.614496 -v 0.321074 0.360830 0.546669 -v -0.546669 0.360830 0.321075 -v 0.541877 0.221240 0.318259 -v 0.453072 0.287158 0.453072 -v 0.575771 0.166623 -0.160243 -v -0.596769 0.166623 0.000000 -v -0.604277 0.221240 0.168176 -v -0.552100 0.287158 0.324265 -v -0.165004 0.437827 0.592873 -v 0.169664 0.360830 0.609621 -v -0.448615 0.360830 0.448614 -v -0.436292 0.437827 0.436292 -v -0.312255 0.437827 0.531651 -v 0.575771 0.166623 0.160243 -v 0.483086 0.122640 -0.283731 -v 0.396438 0.122640 -0.396438 -v 0.283731 0.122640 -0.483086 -v 0.149931 0.122640 -0.538718 -v -0.000000 0.122640 -0.558363 -v -0.149931 0.122640 -0.538718 -v -0.283731 0.122640 -0.483086 -v -0.396438 0.122640 -0.396438 -v -0.483087 0.122640 -0.283731 -v -0.538718 0.122640 -0.149931 -v -0.558363 0.122640 0.000000 -v -0.541877 0.221240 0.318259 -v -0.000000 0.360830 0.631850 -v 0.324265 0.287158 0.552100 -v -0.453072 0.287158 0.453072 -v 0.516317 0.166623 0.303247 -v 0.596768 0.166623 0.000000 -v 0.444680 0.221240 0.444680 -v 0.538718 0.122640 -0.149931 -v -0.538718 0.122640 0.149931 -v -0.516317 0.166623 0.303247 -v -0.444680 0.221240 0.444680 -v -0.169664 0.360830 0.609621 -v 0.171349 0.287158 0.615677 -v -0.324265 0.287158 0.552100 -v -0.321075 0.360830 0.546669 -v 0.538718 0.122640 0.149931 -v 0.558363 0.122640 0.000000 -v 0.449858 0.088629 -0.264215 -v 0.369171 0.088629 -0.369171 -v 0.264215 0.088629 -0.449859 -v 0.139618 0.088629 -0.501662 -v -0.000000 0.088629 -0.519957 -v -0.139618 0.088629 -0.501662 -v -0.264215 0.088629 -0.449859 -v -0.369171 0.088629 -0.369171 -v -0.449859 0.088629 -0.264215 -v -0.501662 0.088629 -0.139618 -v -0.519957 0.088629 0.000000 -v -0.501662 0.088629 0.139618 -v -0.575771 0.166623 0.160243 -v -0.423705 0.166623 0.423705 -v -0.000000 0.287158 0.638129 -v 0.318259 0.221240 0.541877 -v -0.318259 0.221240 0.541877 -v 0.483086 0.122640 0.283731 -v 0.423705 0.166623 0.423705 -v 0.501662 0.088629 -0.139618 -v -0.449859 0.088629 0.264215 -v -0.483087 0.122640 0.283731 -v -0.396438 0.122640 0.396438 -v -0.303247 0.166623 0.516317 -v -0.171350 0.287158 0.615677 -v 0.168176 0.221240 0.604276 -v -0.168177 0.221240 0.604276 -v 0.501662 0.088629 0.139618 -v 0.519957 0.088629 0.000000 -v 0.424299 0.063924 -0.249203 -v 0.348195 0.063924 -0.348195 -v 0.249203 0.063924 -0.424298 -v 0.131685 0.063924 -0.473160 -v -0.000000 0.063924 -0.490415 -v -0.131686 0.063924 -0.473160 -v -0.249203 0.063924 -0.424298 -v -0.348196 0.063924 -0.348195 -v -0.424299 0.063924 -0.249203 -v -0.473160 0.063924 -0.131685 -v -0.490415 0.063924 0.000000 -v -0.473160 0.063924 0.131685 -v -0.424299 0.063924 0.249203 -v -0.283731 0.122640 0.483086 -v -0.000000 0.221240 0.626311 -v 0.303247 0.166623 0.516317 -v -0.160244 0.166623 0.575771 -v 0.449858 0.088629 0.264215 -v 0.396438 0.122640 0.396438 -v 0.473160 0.063924 -0.131685 -v -0.348196 0.063924 0.348195 -v -0.369171 0.088629 0.369171 -v -0.264215 0.088629 0.449859 -v -0.149931 0.122640 0.538718 -v 0.160243 0.166623 0.575771 -v -0.000000 0.166623 0.596769 -v 0.473160 0.063924 0.131685 -v 0.490415 0.063924 0.000000 -v 0.414076 0.047860 -0.243198 -v 0.339803 0.047860 -0.339804 -v 0.243198 0.047860 -0.414076 -v 0.128512 0.047860 -0.461757 -v -0.000000 0.047860 -0.478597 -v -0.128513 0.047860 -0.461757 -v -0.243198 0.047860 -0.414076 -v -0.339804 0.047860 -0.339804 -v -0.414076 0.047860 -0.243198 -v -0.461757 0.047860 -0.128512 -v -0.478597 0.047860 0.000000 -v -0.461757 0.047860 0.128512 -v -0.414076 0.047860 0.243198 -v -0.339804 0.047860 0.339804 -v -0.139618 0.088629 0.501662 -v 0.283731 0.122640 0.483086 -v -0.000000 0.122640 0.558363 -v 0.424299 0.063924 0.249203 -v 0.369171 0.088629 0.369171 -v 0.461756 0.047860 -0.128512 -v -0.243198 0.047860 0.414076 -v -0.249203 0.063924 0.424298 -v -0.131686 0.063924 0.473160 -v -0.000000 0.088629 0.519957 -v 0.149931 0.122640 0.538718 -v 0.461756 0.047860 0.128512 -v 0.478596 0.047860 0.000000 -v 0.410719 0.036005 -0.241228 -v 0.337050 0.036005 -0.337050 -v 0.241227 0.036005 -0.410719 -v 0.127471 0.036005 -0.458017 -v -0.000000 0.036005 -0.474720 -v -0.127471 0.036005 -0.458017 -v -0.241228 0.036005 -0.410719 -v -0.337051 0.036005 -0.337050 -v -0.410719 0.036005 -0.241228 -v -0.458017 0.036005 -0.127471 -v -0.474721 0.036005 0.000000 -v -0.458017 0.036005 0.127471 -v -0.410719 0.036005 0.241228 -v -0.337051 0.036005 0.337050 -v -0.241228 0.036005 0.410719 -v -0.000000 0.063924 0.490415 -v 0.264215 0.088629 0.449859 -v 0.139618 0.088629 0.501662 -v 0.414076 0.047860 0.243198 -v 0.348195 0.063924 0.348195 -v 0.458017 0.036005 -0.127471 -v -0.127471 0.036005 0.458017 -v -0.128513 0.047860 0.461757 -v -0.000000 0.047860 0.478597 -v 0.131685 0.063924 0.473160 -v 0.458017 0.036005 0.127471 -v 0.474720 0.036005 0.000000 -v 0.394137 0.024816 -0.231489 -v 0.323442 0.024816 -0.323442 -v 0.231489 0.024816 -0.394137 -v 0.122324 0.024816 -0.439524 -v -0.000000 0.024816 -0.455554 -v -0.122325 0.024816 -0.439524 -v -0.231489 0.024816 -0.394137 -v -0.323442 0.024816 -0.323442 -v -0.394137 0.024816 -0.231489 -v -0.439524 0.024816 -0.122325 -v -0.455554 0.024816 0.000000 -v -0.439524 0.024816 0.122325 -v -0.394137 0.024816 0.231489 -v -0.323442 0.024816 0.323442 -v -0.231489 0.024816 0.394137 -v -0.122325 0.024816 0.439524 -v 0.128512 0.047860 0.461757 -v 0.249203 0.063924 0.424298 -v 0.410719 0.036005 0.241228 -v 0.339803 0.047860 0.339804 -v 0.439524 0.024816 -0.122325 -v -0.000000 0.036005 0.474720 -v -0.000000 0.024816 0.455554 -v 0.127471 0.036005 0.458017 -v 0.243198 0.047860 0.414076 -v 0.439524 0.024816 0.122325 -v 0.455554 0.024816 0.000000 -v 0.354551 0.014956 -0.208238 -v 0.290957 0.014956 -0.290957 -v 0.208238 0.014956 -0.354551 -v 0.110038 0.014956 -0.395378 -v -0.000000 0.014956 -0.409797 -v -0.110038 0.014956 -0.395378 -v -0.208239 0.014956 -0.354551 -v -0.290957 0.014956 -0.290957 -v -0.354551 0.014956 -0.208238 -v -0.395378 0.014956 -0.110038 -v -0.409797 0.014956 0.000000 -v -0.395378 0.014956 0.110038 -v -0.354551 0.014956 0.208238 -v -0.290957 0.014956 0.290957 -v -0.208239 0.014956 0.354551 -v -0.110038 0.014956 0.395378 -v -0.000000 0.014956 0.409797 -v 0.241227 0.036005 0.410719 -v 0.337050 0.036005 0.337050 -v 0.394137 0.024816 0.231489 -v 0.395378 0.014956 -0.110038 -v 0.122324 0.024816 0.439524 -v 0.110038 0.014956 0.395378 -v 0.231489 0.024816 0.394137 -v 0.395378 0.014956 0.110038 -v 0.409797 0.014956 0.000000 -v 0.282184 0.007090 -0.165735 -v 0.231570 0.007090 -0.231570 -v 0.165735 0.007090 -0.282185 -v 0.087579 0.007090 -0.314679 -v -0.000000 0.007090 -0.326154 -v -0.087579 0.007090 -0.314679 -v -0.165735 0.007090 -0.282185 -v -0.231570 0.007090 -0.231570 -v -0.282184 0.007090 -0.165735 -v -0.314679 0.007090 -0.087579 -v -0.326155 0.007090 0.000000 -v -0.314679 0.007090 0.087579 -v -0.282184 0.007090 0.165735 -v -0.231570 0.007090 0.231570 -v -0.165735 0.007090 0.282185 -v -0.087579 0.007090 0.314679 -v -0.000000 0.007090 0.326154 -v 0.087579 0.007090 0.314679 -v 0.323442 0.024816 0.323442 -v 0.354551 0.014956 0.208238 -v 0.314679 0.007090 -0.087579 -v 0.208238 0.014956 0.354551 -v 0.165735 0.007090 0.282185 -v 0.290957 0.014956 0.290957 -v 0.314679 0.007090 0.087579 -v 0.326154 0.007090 0.000000 -v 0.167259 0.001883 -0.098236 -v 0.137258 0.001883 -0.137259 -v 0.098236 0.001883 -0.167259 -v 0.051910 0.001883 -0.186520 -v -0.000000 0.001883 -0.193322 -v -0.051911 0.001883 -0.186520 -v -0.098237 0.001883 -0.167259 -v -0.137259 0.001883 -0.137259 -v -0.167259 0.001883 -0.098236 -v -0.186520 0.001883 -0.051911 -v -0.193323 0.001883 0.000000 -v -0.186520 0.001883 0.051911 -v -0.167259 0.001883 0.098236 -v -0.137259 0.001883 0.137259 -v -0.098237 0.001883 0.167259 -v -0.051911 0.001883 0.186520 -v -0.000000 0.001883 0.193322 -v 0.051910 0.001883 0.186520 -v 0.098236 0.001883 0.167259 -v 0.282184 0.007090 0.165735 -v 0.186520 0.001883 -0.051911 -v 0.231570 0.007090 0.231570 -v 0.137258 0.001883 0.137259 -v 0.186520 0.001883 0.051911 -v 0.193322 0.001883 0.000000 -v -0.000000 0.000000 0.000000 -v 0.167259 0.001883 0.098236 -v 0.063813 0.861474 0.000000 -v 0.054654 0.888729 0.000000 -v 0.052734 0.888729 0.014691 -v 0.061568 0.861474 0.017135 -v 0.061568 0.861474 -0.017135 -v 0.072979 0.919969 0.020357 -v 0.111968 0.841089 0.000000 -v 0.047296 0.888729 0.027792 -v 0.052734 0.888729 -0.014691 -v 0.108028 0.841089 -0.030065 -v 0.075630 0.919969 0.000000 -v 0.065466 0.919969 0.038494 -v 0.108028 0.841089 0.030065 -v 0.055210 0.861474 0.032427 -v 0.055210 0.861474 -0.032427 -v 0.096873 0.841089 -0.056896 -v 0.100064 0.951211 0.027927 -v 0.089769 0.951211 0.052799 -v 0.183167 0.826023 0.000000 -v 0.176722 0.826023 -0.049184 -v 0.038821 0.888729 0.038821 -v 0.053751 0.919969 0.053751 -v 0.047296 0.888729 -0.027792 -v 0.072979 0.919969 -0.020357 -v 0.158473 0.826023 -0.093076 -v 0.103696 0.951211 0.000000 -v 0.073714 0.951211 0.073714 -v 0.176722 0.826023 0.049184 -v 0.096873 0.841089 0.056896 -v 0.045307 0.861474 0.045307 -v 0.079497 0.841089 -0.079497 -v 0.045307 0.861474 -0.045307 -v 0.130048 0.826023 -0.130048 -v 0.111754 0.978466 0.031195 -v 0.100259 0.978466 0.058974 -v 0.082330 0.978466 0.082330 -v 0.263228 0.813615 0.000000 -v 0.253966 0.813615 -0.070682 -v 0.227741 0.813615 -0.133759 -v 0.027792 0.888729 0.047296 -v 0.038494 0.919969 0.065466 -v 0.052799 0.951211 0.089769 -v 0.038821 0.888729 -0.038821 -v 0.065466 0.919969 -0.038494 -v 0.100064 0.951211 -0.027927 -v 0.186892 0.813615 -0.186892 -v 0.115809 0.978466 0.000000 -v 0.058974 0.978466 0.100259 -v 0.253966 0.813615 0.070682 -v 0.158473 0.826023 0.093076 -v 0.079497 0.841089 0.079497 -v 0.032426 0.861474 0.055210 -v 0.093076 0.826023 -0.158473 -v 0.056896 0.841089 -0.096873 -v 0.032426 0.861474 -0.055210 -v 0.133759 0.813615 -0.227741 -v 0.085811 0.997741 0.023955 -v 0.076985 0.997741 0.045285 -v 0.063219 0.997741 0.063219 -v 0.045285 0.997741 0.076986 -v 0.337972 0.801206 0.000000 -v 0.326081 0.801206 -0.090752 -v 0.292408 0.801206 -0.171740 -v 0.239960 0.801206 -0.239960 -v 0.014691 0.888729 0.052735 -v 0.020357 0.919969 0.072979 -v 0.027927 0.951211 0.100064 -v 0.031195 0.978466 0.111754 -v 0.027792 0.888729 -0.047296 -v 0.053751 0.919969 -0.053751 -v 0.089769 0.951211 -0.052799 -v 0.111754 0.978466 -0.031195 -v 0.171740 0.801206 -0.292408 -v 0.088924 0.997741 0.000000 -v 0.023955 0.997741 0.085811 -v 0.326081 0.801206 0.090752 -v 0.227741 0.813615 0.133759 -v 0.130048 0.826023 0.130048 -v 0.056896 0.841089 0.096873 -v 0.017135 0.861474 0.061568 -v 0.070682 0.813615 -0.253966 -v 0.049184 0.826023 -0.176722 -v 0.030065 0.841089 -0.108029 -v 0.017135 0.861474 -0.061568 -v 0.090752 0.801206 -0.326081 -v -0.000000 1.005054 0.000000 -v 0.393218 0.786140 0.000000 -v 0.379380 0.786140 -0.105586 -v 0.340206 0.786140 -0.199813 -v 0.279184 0.786140 -0.279184 -v 0.199813 0.786140 -0.340206 -v -0.000000 0.888729 0.054654 -v -0.000000 0.919969 0.075630 -v -0.000000 0.951211 0.103696 -v -0.000000 0.978466 0.115809 -v -0.000000 0.997741 0.088925 -v 0.014691 0.888729 -0.052735 -v 0.038494 0.919969 -0.065466 -v 0.073714 0.951211 -0.073714 -v 0.100259 0.978466 -0.058974 -v 0.085811 0.997741 -0.023955 -v 0.105586 0.786140 -0.379381 -v 0.379380 0.786140 0.105586 -v 0.292408 0.801206 0.171740 -v 0.186892 0.813615 0.186892 -v 0.093076 0.826023 0.158473 -v 0.030065 0.841089 0.108029 -v -0.000000 0.861474 0.063813 -v -0.000000 0.801206 -0.337972 -v -0.000000 0.813615 -0.263228 -v -0.000000 0.826023 -0.183167 -v -0.000000 0.841089 -0.111968 -v -0.000000 0.861474 -0.063813 -v -0.000000 0.786140 -0.393218 -v 0.076985 0.997741 -0.045285 -v -0.023955 0.997741 0.085811 -v 0.414784 0.765755 0.000000 -v 0.400190 0.765755 -0.111377 -v 0.358865 0.765755 -0.210772 -v 0.294497 0.765755 -0.294497 -v 0.210772 0.765755 -0.358865 -v 0.111377 0.765755 -0.400190 -v -0.014691 0.888729 0.052735 -v -0.020357 0.919969 0.072979 -v -0.027927 0.951211 0.100064 -v -0.031195 0.978466 0.111754 -v -0.000000 0.888729 -0.054654 -v 0.020357 0.919969 -0.072979 -v 0.052799 0.951211 -0.089769 -v 0.082330 0.978466 -0.082330 -v -0.000000 0.765755 -0.414784 -v 0.063219 0.997741 -0.063219 -v -0.045285 0.997741 0.076986 -v 0.400190 0.765755 0.111377 -v 0.340206 0.786140 0.199813 -v 0.239960 0.801206 0.239960 -v 0.133759 0.813615 0.227741 -v 0.049184 0.826023 0.176722 -v -0.000000 0.841089 0.111968 -v -0.017135 0.861474 0.061568 -v -0.105586 0.786140 -0.379381 -v -0.090752 0.801206 -0.326081 -v -0.070682 0.813615 -0.253966 -v -0.049184 0.826023 -0.176722 -v -0.030066 0.841089 -0.108029 -v -0.017135 0.861474 -0.061568 -v -0.111377 0.765755 -0.400190 -v 0.045285 0.997741 -0.076986 -v -0.063220 0.997741 0.063219 -v 0.414952 0.750806 0.115486 -v 0.430085 0.750806 0.000000 -v 0.414952 0.750806 -0.115486 -v 0.372103 0.750806 -0.218547 -v 0.305360 0.750806 -0.305360 -v 0.218547 0.750806 -0.372103 -v 0.115486 0.750806 -0.414952 -v -0.000000 0.750806 -0.430085 -v -0.027793 0.888729 0.047296 -v -0.038494 0.919969 0.065466 -v -0.052799 0.951211 0.089769 -v -0.058974 0.978466 0.100259 -v -0.014691 0.888729 -0.052735 -v -0.000000 0.919969 -0.075630 -v 0.027927 0.951211 -0.100064 -v 0.058974 0.978466 -0.100259 -v -0.115486 0.750806 -0.414952 -v 0.023955 0.997741 -0.085811 -v -0.076986 0.997741 0.045285 -v 0.372103 0.750806 0.218547 -v 0.358865 0.765755 0.210772 -v 0.279184 0.786140 0.279184 -v 0.171740 0.801206 0.292408 -v 0.070682 0.813615 0.253966 -v -0.000000 0.826023 0.183167 -v -0.030066 0.841089 0.108029 -v -0.032427 0.861474 0.055210 -v -0.210772 0.765755 -0.358865 -v -0.199813 0.786140 -0.340206 -v -0.171740 0.801206 -0.292408 -v -0.133759 0.813615 -0.227741 -v -0.093076 0.826023 -0.158473 -v -0.056896 0.841089 -0.096873 -v -0.032427 0.861474 -0.055210 -v -0.218547 0.750806 -0.372103 -v 0.031195 0.978466 -0.111754 -v -0.000000 0.997741 -0.088925 -v -0.082331 0.978466 0.082330 -v -0.085811 0.997741 0.023955 -v 0.305360 0.750806 0.305360 -v 0.294497 0.765755 0.294497 -v -0.038821 0.888729 0.038821 -v -0.053751 0.919969 0.053751 -v -0.073714 0.951211 0.073714 -v -0.027793 0.888729 -0.047296 -v -0.020357 0.919969 -0.072979 -v -0.000000 0.951211 -0.103696 -v -0.305360 0.750806 -0.305360 -v -0.294497 0.765755 -0.294497 -v -0.000000 0.978466 -0.115809 -v -0.023955 0.997741 -0.085811 -v -0.100259 0.978466 0.058974 -v -0.088925 0.997741 0.000000 -v 0.210772 0.765755 0.358865 -v 0.218547 0.750806 0.372103 -v 0.199813 0.786140 0.340206 -v 0.090752 0.801206 0.326081 -v -0.000000 0.813615 0.263228 -v -0.049184 0.826023 0.176722 -v -0.056896 0.841089 0.096873 -v -0.045307 0.861474 0.045307 -v -0.279185 0.786140 -0.279184 -v -0.239960 0.801206 -0.239960 -v -0.186892 0.813615 -0.186892 -v -0.130049 0.826023 -0.130048 -v -0.079497 0.841089 -0.079497 -v -0.045307 0.861474 -0.045307 -v -0.372103 0.750806 -0.218547 -v -0.358865 0.765755 -0.210772 -v -0.031195 0.978466 -0.111754 -v -0.045285 0.997741 -0.076986 -v -0.111754 0.978466 0.031195 -v -0.085811 0.997741 -0.023955 -v 0.111377 0.765755 0.400190 -v 0.115486 0.750806 0.414952 -v -0.047296 0.888729 0.027792 -v -0.065466 0.919969 0.038494 -v -0.089770 0.951211 0.052799 -v -0.038821 0.888729 -0.038821 -v -0.038494 0.919969 -0.065466 -v -0.027927 0.951211 -0.100064 -v -0.414952 0.750806 -0.115486 -v -0.400190 0.765755 -0.111377 -v -0.058974 0.978466 -0.100259 -v -0.063220 0.997741 -0.063219 -v -0.115809 0.978466 0.000000 -v -0.076986 0.997741 -0.045285 -v 0.105586 0.786140 0.379381 -v -0.000000 0.765755 0.414784 -v -0.000000 0.750806 0.430085 -v -0.000000 0.801206 0.337972 -v -0.070682 0.813615 0.253966 -v -0.093076 0.826023 0.158473 -v -0.079497 0.841089 0.079497 -v -0.055210 0.861474 0.032427 -v -0.340206 0.786140 -0.199813 -v -0.292408 0.801206 -0.171740 -v -0.227741 0.813615 -0.133759 -v -0.158473 0.826023 -0.093076 -v -0.096873 0.841089 -0.056896 -v -0.055210 0.861474 -0.032427 -v -0.430085 0.750806 0.000000 -v -0.414784 0.765755 0.000000 -v -0.052799 0.951211 -0.089769 -v -0.082331 0.978466 -0.082330 -v -0.100064 0.951211 0.027927 -v -0.111754 0.978466 -0.031195 -v -0.000000 0.786140 0.393218 -v -0.115486 0.750806 0.414952 -v -0.111377 0.765755 0.400190 -v -0.052735 0.888729 0.014691 -v -0.072979 0.919969 0.020357 -v -0.047296 0.888729 -0.027792 -v -0.053751 0.919969 -0.053751 -v -0.414952 0.750806 0.115486 -v -0.400190 0.765755 0.111377 -v -0.379381 0.786140 -0.105586 -v -0.073714 0.951211 -0.073714 -v -0.100259 0.978466 -0.058974 -v -0.103696 0.951211 0.000000 -v -0.105586 0.786140 0.379381 -v -0.218547 0.750806 0.372103 -v -0.210772 0.765755 0.358865 -v -0.090752 0.801206 0.326081 -v -0.133759 0.813615 0.227741 -v -0.130049 0.826023 0.130048 -v -0.096873 0.841089 0.056896 -v -0.061568 0.861474 0.017135 -v -0.326081 0.801206 -0.090752 -v -0.253966 0.813615 -0.070682 -v -0.176722 0.826023 -0.049184 -v -0.108029 0.841089 -0.030065 -v -0.061568 0.861474 -0.017135 -v -0.372103 0.750806 0.218547 -v -0.358865 0.765755 0.210772 -v -0.393219 0.786140 0.000000 -v -0.089770 0.951211 -0.052799 -v -0.100064 0.951211 -0.027927 -v -0.199813 0.786140 0.340206 -v -0.305360 0.750806 0.305360 -v -0.294497 0.765755 0.294497 -v -0.054655 0.888729 0.000000 -v -0.075630 0.919969 0.000000 -v -0.052735 0.888729 -0.014691 -v -0.065466 0.919969 -0.038494 -v -0.379381 0.786140 0.105586 -v -0.171740 0.801206 0.292408 -v -0.279185 0.786140 0.279184 -v -0.186892 0.813615 0.186892 -v -0.158473 0.826023 0.093076 -v -0.108029 0.841089 0.030065 -v -0.063813 0.861474 0.000000 -v -0.337972 0.801206 0.000000 -v -0.263228 0.813615 0.000000 -v -0.183167 0.826023 0.000000 -v -0.111968 0.841089 0.000000 -v -0.340206 0.786140 0.199813 -v -0.072979 0.919969 -0.020357 -v -0.239960 0.801206 0.239960 -v -0.326081 0.801206 0.090752 -v -0.292408 0.801206 0.171740 -v -0.227741 0.813615 0.133759 -v -0.176722 0.826023 0.049184 -v -0.253966 0.813615 0.070682 -v -0.526706 0.651362 -0.039883 -v -0.534329 0.646030 0.000000 -v -0.619922 0.238069 -0.071790 -v -0.624826 0.259599 -0.063813 -v -0.638129 0.287158 0.000000 -v -0.631184 0.277569 0.039883 -v -0.501666 0.699221 -0.063813 -v -0.508714 0.682112 -0.071712 -v -0.611709 0.194244 0.000000 -v -0.608883 0.198681 0.039883 -v -0.517593 0.664661 0.063813 -v -0.508714 0.682112 0.071712 -v -0.631184 0.277569 -0.039883 -v -0.624828 0.259599 0.063813 -v -0.615480 0.216617 0.063578 -v -0.615553 0.216807 -0.063813 -v -0.517593 0.664661 -0.063813 -v -0.498530 0.712498 -0.039883 -v -0.619922 0.238069 0.071790 -v -0.526706 0.651362 0.039883 -v -0.608884 0.198682 -0.039883 -v 0.605100 0.399712 0.137265 -v 0.613258 0.341675 0.154354 -v 0.605956 0.463769 0.000000 -v 0.600959 0.444810 -0.085753 -v 0.613258 0.341675 -0.154354 -v 0.605101 0.399712 -0.137265 -v 0.600960 0.444810 0.085753 -v 0.121642 0.750009 -0.437072 -v -0.000000 0.750009 -0.453012 -v 0.453011 0.750009 0.000000 -v 0.437073 0.750009 -0.121642 -v -0.453012 0.750009 0.000000 -v -0.437073 0.750009 -0.121642 -v -0.230198 0.750009 0.391939 -v -0.321639 0.750009 0.321639 -v -0.391940 0.750009 0.230197 -v -0.437073 0.750009 0.121642 -v 0.121642 0.750009 0.437072 -v -0.000000 0.750009 0.453012 -v -0.121642 0.750009 0.437072 -v 0.437073 0.750009 0.121642 -v 0.391939 0.750009 0.230197 -v 0.321638 0.750009 -0.321639 -v 0.230197 0.750009 -0.391940 -v -0.121642 0.750009 -0.437072 -v 0.391939 0.750009 -0.230197 -v 0.321638 0.750009 0.321639 -v 0.230197 0.750009 0.391940 -v -0.230198 0.750009 -0.391939 -v -0.501255 0.717792 0.000000 -v 0.617684 0.235930 0.085941 -v 0.625577 0.219883 0.000000 -v -0.321639 0.750009 -0.321639 -v -0.391940 0.750009 -0.230197 -v -0.498530 0.712498 0.039883 -v -0.501667 0.699221 0.063813 -v 0.617684 0.235930 -0.085941 -v 0.619427 0.283145 -0.137236 -v 0.619427 0.283145 0.137236 -vn -0.901883 0.415418 0.118168 -vn -0.905637 0.407056 0.118656 -vn -0.877041 0.418744 0.235298 -vn 0.058443 -0.998260 0.000732 -vn 0.015107 -0.999878 0.000183 -vn 0.014557 -0.949278 0.314035 -vn 0.056703 -0.947539 0.314524 -vn 0.162053 -0.986755 0.002014 -vn 0.157933 -0.933592 0.321604 -vn 0.392376 -0.919767 0.004334 -vn 0.378307 -0.856655 0.350688 -vn 0.783776 -0.620991 0.005249 -vn 0.726829 -0.553880 0.406079 -vn 0.994812 -0.101627 0.001984 -vn 0.908139 -0.082766 0.410321 -vn 0.003082 -0.939787 0.341685 -vn 0.002167 -0.619495 0.784967 -vn 0.011536 -0.679403 0.733634 -vn 0.044679 -0.675588 0.735923 -vn 0.123325 -0.652272 0.747856 -vn 0.275399 -0.556871 0.783593 -vn 0.460067 -0.316263 0.829615 -vn 0.563036 -0.041200 0.825373 -vn -0.000427 0.122166 0.992492 -vn 0.000397 0.003632 0.999969 -vn 0.002869 0.011841 0.999908 -vn 0.004852 0.029298 0.999542 -vn -0.008179 0.053499 0.998505 -vn -0.046510 0.041536 0.998047 -vn -0.039155 0.003113 0.999207 -vn -0.850551 0.473769 -0.228217 -vn -0.897885 0.424177 -0.117649 -vn -0.880886 0.473281 0.000000 -vn -0.013611 0.682394 0.730827 -vn -0.053896 0.680441 0.730796 -vn -0.147557 0.656789 0.739464 -vn -0.325968 0.560564 0.761223 -vn -0.537645 0.315806 0.781762 -vn -0.611530 0.029939 0.790613 -vn -0.904172 0.427137 0.000000 -vn -0.897885 0.424146 0.117618 -vn -0.020112 0.949461 0.313150 -vn -0.081820 0.945433 0.315287 -vn -0.227699 0.916379 0.329173 -vn -0.504196 0.785302 0.359203 -vn -0.810633 0.443220 0.382611 -vn -0.921232 0.039705 0.386944 -vn -0.020569 0.949400 -0.313334 -vn -0.021729 0.999756 -0.000092 -vn -0.004242 0.950468 -0.310770 -vn -0.088260 0.996094 -0.000488 -vn -0.246895 0.969024 -0.001343 -vn -0.549730 0.835322 -0.002350 -vn -0.880673 0.473647 -0.001984 -vn -0.999084 0.042146 -0.000610 -vn -0.877041 0.418744 -0.235298 -vn -0.920286 0.391156 0.000000 -vn -0.905637 0.407056 -0.118656 -vn -0.083132 0.945006 -0.316202 -vn -0.230201 0.914823 -0.331797 -vn -0.505570 0.782800 -0.362743 -vn -0.808710 0.444960 -0.384625 -vn -0.920835 0.042055 -0.387646 -vn -0.897885 0.424146 -0.117618 -vn -0.901883 0.415448 -0.118168 -vn -0.014161 0.682394 -0.730796 -vn -0.055361 0.680074 -0.731010 -vn -0.150029 0.655660 -0.739982 -vn -0.327616 0.560594 -0.760491 -vn -0.537431 0.320933 -0.779809 -vn -0.611988 0.033387 -0.790155 -vn 0.015168 -0.949339 -0.313852 -vn 0.011902 -0.679403 -0.733634 -vn 0.003265 -0.939817 -0.341594 -vn 0.000183 0.004212 -0.999969 -vn 0.003510 0.014008 -0.999878 -vn 0.005921 0.035951 -0.999329 -vn -0.010132 0.064333 -0.997864 -vn -0.051576 0.048463 -0.997467 -vn -0.041597 0.003998 -0.999115 -vn 0.003082 -0.620106 -0.784478 -vn -0.000031 0.122440 -0.992462 -vn -0.897885 0.424177 0.117649 -vn 0.046449 -0.674398 -0.736869 -vn 0.125980 -0.648946 -0.750298 -vn 0.275430 -0.552477 -0.786676 -vn 0.455519 -0.320536 -0.830500 -vn 0.561693 -0.046480 -0.826014 -vn -0.888668 0.391644 0.238441 -vn 0.058046 -0.947630 -0.314005 -vn 0.159948 -0.933836 -0.319865 -vn 0.380169 -0.857753 -0.345927 -vn 0.725547 -0.560930 -0.398602 -vn 0.908597 -0.089236 -0.407971 -vn 0.003235 -0.999969 0.000031 -vn 0.973144 0.230110 0.000824 -vn 0.890896 0.211737 0.401776 -vn 0.912900 0.408094 0.002533 -vn 0.836970 0.380932 0.392834 -vn 0.829035 0.559160 0.003784 -vn 0.764519 0.528550 0.368969 -vn 0.718650 0.695334 0.003937 -vn 0.668294 0.663717 0.335917 -vn 0.579577 0.814905 0.002838 -vn 0.542650 0.779687 0.312357 -vn 0.495163 0.868770 0.002258 -vn 0.458052 0.820643 0.341624 -vn 0.561205 0.137028 0.816218 -vn 0.532029 0.253456 0.807886 -vn 0.497543 0.363445 0.787591 -vn 0.449538 0.472060 0.758293 -vn 0.373669 0.563555 0.736686 -vn 0.289041 0.531114 0.796442 -vn -0.023225 -0.005249 0.999695 -vn -0.016785 -0.010254 0.999786 -vn -0.011444 -0.012940 0.999847 -vn -0.009796 -0.013276 0.999847 -vn -0.014801 -0.013916 0.999786 -vn -0.089755 -0.176122 0.980255 -vn -0.585772 -0.152379 0.795984 -vn -0.538896 -0.288766 0.791314 -vn -0.484146 -0.407910 0.774071 -vn -0.424635 -0.509781 0.748161 -vn -0.355907 -0.584765 0.728935 -vn -0.889828 -0.237159 0.389782 -vn -0.808740 -0.446852 0.382366 -vn -0.702475 -0.613269 0.361095 -vn -0.590625 -0.734855 0.333293 -vn -0.483291 -0.816767 0.315104 -vn -0.912076 0.409955 0.000000 -vn -0.965606 -0.259987 -0.000458 -vn -0.872433 -0.488693 -0.001465 -vn -0.748436 -0.663167 -0.002197 -vn -0.621601 -0.783288 -0.002136 -vn -0.507065 -0.861873 -0.001251 -vn -0.438215 -0.854366 0.279183 -vn -0.456130 -0.889889 -0.000732 -vn -0.889126 -0.238868 -0.390332 -vn -0.807001 -0.448531 -0.384075 -vn -0.700980 -0.613392 -0.363750 -vn -0.590442 -0.733757 -0.336039 -vn -0.484787 -0.815332 -0.316477 -vn -0.440962 -0.852931 -0.279305 -vn -0.359691 -0.584185 -0.727531 -vn -0.358074 -0.682241 -0.637410 -vn -0.585467 -0.154668 -0.795770 -vn -0.538499 -0.291696 -0.790490 -vn -0.484512 -0.409772 -0.772851 -vn -0.426496 -0.510056 -0.746910 -vn -0.909543 -0.399274 -0.115207 -vn -0.971191 -0.204688 -0.121891 -vn -0.912931 -0.326609 -0.244606 -vn -0.020478 -0.017853 -0.999603 -vn -0.024537 -0.005737 -0.999664 -vn -0.020844 -0.012207 -0.999695 -vn -0.017548 -0.016846 -0.999695 -vn -0.016724 -0.018097 -0.999695 -vn -0.909116 -0.400311 0.115055 -vn -0.873775 -0.472610 0.114475 -vn -0.795892 -0.566485 0.213538 -vn -0.353069 -0.684103 0.638203 -vn 0.559679 0.139714 -0.816828 -vn 0.528581 0.255501 -0.809473 -vn 0.494217 0.362987 -0.789911 -vn 0.449049 0.469283 -0.760308 -vn 0.378246 0.560869 -0.736412 -vn -0.091983 -0.174383 -0.980346 -vn 0.295267 0.530625 -0.794488 -vn 0.890500 0.214759 -0.401044 -vn 0.836634 0.384075 -0.390515 -vn 0.765191 0.530198 -0.365123 -vn 0.671041 0.663228 -0.331339 -vn 0.547929 0.777642 -0.308206 -vn 0.464522 0.818842 -0.337199 -vn 0.931486 0.265572 -0.248543 -vn 0.939543 0.342357 0.000000 -vn 0.947539 0.295114 -0.122684 -vn -0.351421 0.936186 0.001953 -vn -0.144444 0.989502 0.003174 -vn -0.126743 0.878811 0.459975 -vn -0.716758 0.697287 -0.000946 -vn -0.299997 0.838313 0.455214 -vn -0.621876 0.660207 0.421155 -vn -0.901822 0.432081 -0.004517 -vn -0.807031 0.443434 0.389904 -vn -0.930204 0.366863 -0.008484 -vn -0.824549 0.383312 0.416059 -vn -0.850673 0.525529 -0.011628 -vn -0.722465 0.508988 0.467910 -vn -0.668447 0.743645 -0.011139 -vn -0.531449 0.686514 0.496170 -vn -0.116459 0.505448 0.854946 -vn -0.258400 0.470656 0.843593 -vn -0.407605 0.396985 0.822321 -vn -0.450270 0.352367 0.820399 -vn -0.385876 0.395734 0.833338 -vn -0.270669 0.487838 0.829890 -vn 0.141606 -0.001190 0.989898 -vn -0.067690 0.525346 0.848170 -vn 0.989593 0.100253 0.103122 -vn 0.970244 0.213324 0.114475 -vn 0.960418 0.152654 0.232917 -vn 0.241829 0.092502 0.965880 -vn 0.209296 0.170660 0.962828 -vn 0.096194 0.178625 0.979186 -vn 0.009552 0.154332 0.987945 -vn -0.000122 0.151952 0.988372 -vn 0.361248 -0.477279 0.801019 -vn 0.607929 -0.282540 0.741997 -vn 0.679220 -0.106754 0.726096 -vn 0.583911 -0.078524 0.807978 -vn 0.402722 -0.205237 0.891995 -vn 0.279519 -0.338694 0.898404 -vn 0.488601 -0.768700 0.412671 -vn 0.784570 -0.501511 0.364544 -vn 0.893918 -0.279611 0.350291 -vn 0.861415 -0.285287 0.420179 -vn 0.679373 -0.540452 0.496323 -vn 0.458327 -0.754540 0.469588 -vn 0.524155 -0.851588 -0.000153 -vn 0.827143 -0.561968 0.001679 -vn 0.943205 -0.332133 0.003479 -vn 0.933256 -0.359081 0.005737 -vn 0.756859 -0.653493 0.006470 -vn 0.492843 -0.870083 0.004120 -vn 0.322489 -0.946562 -0.001129 -vn 0.912839 -0.326609 0.244942 -vn 0.824396 -0.565996 0.000000 -vn 0.894162 -0.447676 0.000000 -vn 0.486557 -0.770501 -0.411756 -vn 0.783990 -0.504074 -0.362285 -vn 0.895199 -0.281899 -0.345134 -vn 0.863735 -0.289010 -0.412824 -vn 0.682119 -0.544267 -0.488296 -vn 0.461928 -0.758202 -0.460128 -vn 0.357463 -0.479141 -0.801599 -vn 0.605884 -0.285867 -0.742393 -vn 0.679739 -0.108646 -0.725333 -vn 0.583636 -0.077883 -0.808222 -vn 0.401440 -0.199225 -0.893918 -vn 0.281961 -0.330638 -0.900632 -vn 0.135228 -0.002380 -0.990783 -vn 0.235755 0.091128 -0.967498 -vn 0.200720 0.170629 -0.964660 -vn 0.086123 0.184576 -0.979003 -vn 0.000671 0.174322 -0.984680 -vn -0.007141 0.169012 -0.985565 -vn 0.966613 -0.042848 -0.252602 -vn 0.960418 0.152654 -0.232917 -vn 0.989593 0.100223 -0.103092 -vn -0.119297 0.503342 -0.855770 -vn -0.261269 0.472793 -0.841517 -vn -0.414075 0.399792 -0.817713 -vn -0.459700 0.357036 -0.813105 -vn -0.394024 0.408490 -0.823298 -vn -0.277871 0.487381 -0.827754 -vn -0.072207 0.520127 -0.851009 -vn -0.298654 0.840297 -0.452376 -vn -0.617512 0.663961 -0.421613 -vn -0.801324 0.450209 -0.393872 -vn -0.819422 0.389691 -0.420270 -vn -0.721274 0.506912 -0.471969 -vn -0.538347 0.670644 -0.510239 -vn -0.482009 0.876125 -0.005127 -vn -0.394635 0.815363 0.423536 -vn -0.321909 0.946745 -0.002594 -vn -0.255287 0.921995 0.291086 -vn 0.004242 0.999969 0.000397 -vn -0.002960 0.999939 -0.010102 -vn 0.853450 0.521073 -0.007050 -vn 0.392041 0.688986 -0.609546 -vn 0.805170 -0.592517 -0.023621 -vn 0.588763 -0.206122 -0.781579 -vn 0.681478 -0.731040 -0.033296 -vn 0.485031 -0.441237 -0.754967 -vn -0.206824 0.638203 0.741539 -vn -0.129490 0.862056 0.489944 -vn -0.033418 0.999176 0.022340 -vn 0.047700 0.864040 -0.501114 -vn 0.099307 0.538743 -0.836573 -vn 0.053560 0.251839 -0.966277 -vn 0.020112 0.322611 0.946287 -vn 0.021943 0.748894 0.662282 -vn -0.025941 0.995605 0.089908 -vn -0.059175 0.930876 -0.360424 -vn -0.080172 0.784448 -0.614948 -vn -0.142582 0.557604 -0.817743 -vn 0.281747 -0.174993 0.943388 -vn 0.303903 0.444136 0.842830 -vn 0.034333 0.983764 0.175970 -vn -0.113865 0.956420 -0.268777 -vn -0.155339 0.858852 -0.488021 -vn -0.207404 0.641865 -0.738182 -vn 0.467238 -0.683187 0.561144 -vn 0.699515 0.004364 0.714560 -vn 0.355296 0.891934 0.279641 -vn -0.170354 0.971465 -0.164861 -vn -0.245552 0.901181 -0.357097 -vn -0.248726 0.651082 -0.717063 -vn 0.494430 -0.869167 0.006317 -vn 0.933134 -0.358898 0.019868 -vn 0.703146 0.710685 0.021790 -vn -0.203650 0.978942 -0.012574 -vn -0.326456 0.942869 -0.066073 -vn -0.397595 0.857082 -0.327525 -vn 0.460616 -0.712546 -0.529221 -vn 0.695486 -0.098544 -0.711722 -vn 0.397534 0.853816 -0.336070 -vn -0.198248 0.963439 0.180151 -vn -0.306833 0.888516 0.341075 -vn -0.393689 0.807672 0.438887 -vn 0.276559 -0.211951 -0.937315 -vn 0.294626 0.366161 -0.882656 -vn 0.046632 0.973235 -0.224982 -vn -0.146733 0.912168 0.382611 -vn -0.202643 0.700797 0.683950 -vn -0.232673 0.506485 0.830226 -vn 0.011689 0.309397 -0.950835 -vn 0.013245 0.708640 -0.705435 -vn -0.027589 0.995758 -0.087680 -vn -0.047395 0.829371 0.556658 -vn -0.015259 0.386608 0.922086 -vn 0.004639 0.119510 0.992798 -vn -0.211035 0.631001 -0.746513 -vn -0.138768 0.848781 -0.510147 -vn -0.024995 0.999664 -0.001190 -vn 0.120426 0.724570 0.678579 -vn 0.260750 0.006531 0.965361 -vn 0.272500 -0.255898 0.927488 -vn -0.395581 0.809961 -0.432905 -vn -0.258827 0.918851 -0.297800 -vn 0.004730 0.999878 0.013031 -vn 0.466628 0.599780 0.649983 -vn 0.621937 -0.400861 0.672628 -vn 0.551042 -0.592181 0.587878 -vn 0.649068 0.384991 -0.656087 -vn 0.055971 0.691214 0.720450 -vn -0.115940 0.804590 0.582354 -vn -0.262185 0.897946 0.353435 -vn -0.341655 0.885006 -0.316263 -vn -0.081423 0.793085 -0.603626 -vn -0.155675 0.857753 -0.489883 -vn 0.728690 0.365978 0.578784 -vn 0.318674 0.539384 0.779382 -vn 0.953551 0.300150 -0.024415 -vn -0.133976 0.836818 -0.530808 -vn 0.095401 0.667959 -0.738029 -vn 0.000000 0.999969 0.000000 -vn -0.992523 -0.122013 0.000000 -vn -0.937346 -0.348338 0.000000 -vn -0.905148 -0.348827 -0.242836 -vn -0.958617 -0.122227 -0.257057 -vn -0.832057 0.554674 0.000000 -vn -0.803217 0.555376 -0.215339 -vn -0.048616 0.998810 0.000000 -vn -0.046236 0.998840 -0.012726 -vn 0.544267 0.838893 0.000000 -vn 0.525376 0.839106 0.140843 -vn 0.783471 0.621387 0.000000 -vn 0.756371 0.621845 0.202918 -vn 0.880886 0.473281 0.000000 -vn 0.850551 0.473769 0.228217 -vn -0.810907 -0.349376 -0.469375 -vn -0.859004 -0.122410 -0.497085 -vn -0.719657 0.555559 -0.416425 -vn -0.041749 0.998810 -0.024415 -vn 0.470077 0.839625 0.272011 -vn 0.677236 0.622608 0.391980 -vn 0.761803 0.474471 0.440962 -vn -0.662465 -0.349620 -0.662465 -vn -0.701773 -0.122440 -0.701773 -vn -0.587878 0.555650 -0.587878 -vn -0.034272 0.998810 -0.034272 -vn 0.383831 0.839808 0.383831 -vn 0.553148 0.622913 0.553148 -vn 0.622303 0.474776 0.622303 -vn -0.469375 -0.349376 -0.810907 -vn -0.497085 -0.122379 -0.859004 -vn -0.416425 0.555559 -0.719657 -vn -0.024415 0.998810 -0.041749 -vn 0.272011 0.839625 0.470077 -vn 0.391980 0.622608 0.677236 -vn 0.440962 0.474471 0.761834 -vn -0.242836 -0.348827 -0.905148 -vn -0.257057 -0.122227 -0.958617 -vn -0.215339 0.555376 -0.803217 -vn -0.012726 0.998840 -0.046205 -vn 0.140843 0.839106 0.525376 -vn 0.202918 0.621845 0.756371 -vn 0.228217 0.473769 0.850551 -vn 0.000000 -0.348338 -0.937346 -vn 0.000000 -0.122013 -0.992523 -vn 0.000000 0.554674 -0.832057 -vn 0.000000 0.998810 -0.048616 -vn 0.000000 0.838893 0.544267 -vn 0.000000 0.621387 0.783471 -vn 0.000000 0.473281 0.880886 -vn 0.242836 -0.348827 -0.905148 -vn 0.257057 -0.122227 -0.958617 -vn 0.215308 0.555376 -0.803217 -vn 0.012726 0.998840 -0.046205 -vn -0.140843 0.839106 0.525376 -vn -0.202918 0.621845 0.756340 -vn -0.228217 0.473769 0.850551 -vn 0.469375 -0.349376 -0.810907 -vn 0.497085 -0.122379 -0.859004 -vn 0.416425 0.555559 -0.719657 -vn 0.024415 0.998810 -0.041749 -vn -0.272011 0.839625 0.470077 -vn -0.391980 0.622608 0.677236 -vn -0.440962 0.474502 0.761803 -vn 0.662465 -0.349620 -0.662465 -vn 0.701773 -0.122471 -0.701773 -vn 0.587878 0.555650 -0.587878 -vn 0.034272 0.998810 -0.034272 -vn -0.383831 0.839808 0.383831 -vn -0.553148 0.622913 0.553148 -vn -0.622303 0.474776 0.622303 -vn 0.810907 -0.349406 -0.469375 -vn 0.859004 -0.122379 -0.497085 -vn 0.719657 0.555559 -0.416425 -vn 0.041749 0.998810 -0.024415 -vn -0.470077 0.839625 0.272011 -vn -0.677236 0.622608 0.391980 -vn -0.761803 0.474471 0.440962 -vn 0.905148 -0.348827 -0.242836 -vn 0.958617 -0.122227 -0.257057 -vn 0.803217 0.555376 -0.215339 -vn 0.046205 0.998840 -0.012726 -vn -0.525376 0.839106 0.140843 -vn -0.756340 0.621876 0.202918 -vn -0.850551 0.473769 0.228217 -vn 0.937346 -0.348338 0.000000 -vn 0.992523 -0.122013 0.000000 -vn 0.832026 0.554674 0.000000 -vn 0.048616 0.998810 0.000000 -vn -0.544267 0.838893 0.000000 -vn -0.783471 0.621387 0.000000 -vn 0.905148 -0.348827 0.242836 -vn 0.958617 -0.122227 0.257057 -vn 0.803217 0.555376 0.215308 -vn 0.046205 0.998840 0.012726 -vn -0.525376 0.839106 -0.140843 -vn -0.756340 0.621876 -0.202918 -vn 0.810907 -0.349406 0.469375 -vn 0.859004 -0.122379 0.497085 -vn 0.719657 0.555559 0.416425 -vn 0.041749 0.998810 0.024415 -vn -0.470077 0.839625 -0.272011 -vn -0.677236 0.622608 -0.391980 -vn -0.761803 0.474471 -0.440962 -vn 0.662465 -0.349620 0.662465 -vn 0.701773 -0.122471 0.701773 -vn 0.587878 0.555650 0.587878 -vn 0.034272 0.998810 0.034272 -vn -0.383831 0.839808 -0.383831 -vn -0.553148 0.622913 -0.553148 -vn -0.622303 0.474776 -0.622303 -vn 0.469375 -0.349376 0.810907 -vn 0.497085 -0.122379 0.859004 -vn 0.416425 0.555559 0.719657 -vn 0.024415 0.998810 0.041749 -vn -0.272011 0.839625 -0.470077 -vn -0.391980 0.622608 -0.677236 -vn -0.440962 0.474471 -0.761803 -vn 0.242836 -0.348827 0.905148 -vn 0.257057 -0.122227 0.958617 -vn 0.215339 0.555376 0.803217 -vn 0.012726 0.998840 0.046205 -vn -0.140843 0.839106 -0.525376 -vn -0.202918 0.621845 -0.756371 -vn -0.228217 0.473769 -0.850551 -vn 0.000000 -0.348338 0.937346 -vn 0.000000 -0.122013 0.992523 -vn 0.000000 0.554674 0.832057 -vn 0.000000 0.998810 0.048616 -vn 0.000000 0.838893 -0.544267 -vn 0.000000 0.621387 -0.783471 -vn 0.000000 0.473281 -0.880886 -vn -0.242836 -0.348827 0.905148 -vn -0.257057 -0.122227 0.958617 -vn -0.215308 0.555376 0.803217 -vn -0.012726 0.998840 0.046205 -vn 0.140843 0.839106 -0.525376 -vn 0.202918 0.621845 -0.756371 -vn 0.228217 0.473769 -0.850551 -vn -0.469375 -0.349376 0.810907 -vn -0.497085 -0.122379 0.859004 -vn -0.416425 0.555559 0.719657 -vn -0.024415 0.998810 0.041749 -vn 0.272011 0.839625 -0.470077 -vn 0.391980 0.622608 -0.677236 -vn 0.440962 0.474471 -0.761803 -vn -0.662465 -0.349620 0.662465 -vn -0.701773 -0.122440 0.701773 -vn -0.587878 0.555650 0.587878 -vn -0.034272 0.998810 0.034272 -vn 0.383831 0.839808 -0.383831 -vn 0.553148 0.622913 -0.553148 -vn 0.622303 0.474776 -0.622303 -vn -0.810907 -0.349376 0.469375 -vn -0.859004 -0.122410 0.497085 -vn -0.719657 0.555528 0.416425 -vn -0.041749 0.998810 0.024415 -vn 0.470077 0.839625 -0.272011 -vn 0.677236 0.622639 -0.391980 -vn 0.761803 0.474471 -0.440962 -vn -0.905148 -0.348827 0.242836 -vn -0.958617 -0.122227 0.257057 -vn -0.803217 0.555376 0.215339 -vn -0.046236 0.998840 0.012726 -vn 0.525376 0.839106 -0.140843 -vn 0.756371 0.621845 -0.202918 -vn 0.850551 0.473769 -0.228217 -vn 0.908292 0.418256 0.000000 -vn 0.877041 0.418744 0.235298 -vn 0.920286 0.391156 0.000000 -vn 0.888668 0.391644 0.238441 -vn 0.907315 0.342753 0.243446 -vn 0.785638 0.419416 0.454756 -vn 0.796075 0.392285 0.460799 -vn 0.812830 0.343333 0.470504 -vn 0.931486 0.265542 0.248543 -vn 0.834162 0.266366 0.482864 -vn 0.855312 0.152379 0.495132 -vn 0.966613 -0.042848 0.252602 -vn 0.864498 -0.045808 0.500504 -vn 0.641804 0.419691 0.641804 -vn 0.650349 0.392499 0.650349 -vn 0.664052 0.343577 0.664052 -vn 0.681509 0.266579 0.681509 -vn 0.698813 0.152501 0.698813 -vn 0.706351 -0.045869 0.706351 -vn 0.454756 0.419416 0.785638 -vn 0.460799 0.392285 0.796075 -vn 0.470504 0.343333 0.812830 -vn 0.482864 0.266366 0.834162 -vn 0.495132 0.152409 0.855312 -vn 0.500504 -0.045808 0.864498 -vn 0.235298 0.418744 0.877041 -vn 0.238441 0.391644 0.888668 -vn 0.243446 0.342753 0.907315 -vn 0.249855 0.265908 0.931028 -vn 0.256172 0.152104 0.954558 -vn 0.258980 -0.045717 0.964782 -vn 0.000000 0.418256 0.908292 -vn 0.000000 0.391156 0.920286 -vn 0.000000 0.342357 0.939543 -vn 0.000000 0.265542 0.964080 -vn 0.000000 0.151891 0.988372 -vn 0.000000 -0.045656 0.998932 -vn -0.235298 0.418744 0.877041 -vn -0.238441 0.391644 0.888668 -vn -0.243446 0.342753 0.907315 -vn -0.249855 0.265877 0.931028 -vn -0.256172 0.152104 0.954558 -vn -0.258980 -0.045717 0.964782 -vn -0.454756 0.419416 0.785638 -vn -0.460799 0.392285 0.796075 -vn -0.470504 0.343333 0.812830 -vn -0.482864 0.266366 0.834162 -vn -0.495132 0.152379 0.855312 -vn -0.500504 -0.045808 0.864498 -vn -0.641804 0.419691 0.641804 -vn -0.650349 0.392499 0.650349 -vn -0.664052 0.343577 0.664052 -vn -0.681509 0.266579 0.681509 -vn -0.698813 0.152501 0.698813 -vn -0.706351 -0.045869 0.706351 -vn -0.785638 0.419416 0.454756 -vn -0.796075 0.392285 0.460799 -vn -0.812830 0.343364 0.470504 -vn -0.834162 0.266366 0.482864 -vn -0.855312 0.152379 0.495132 -vn -0.864498 -0.045808 0.500504 -vn -0.907315 0.342753 0.243446 -vn -0.931028 0.265908 0.249855 -vn -0.954558 0.152104 0.256172 -vn -0.964782 -0.045717 0.258980 -vn -0.939543 0.342357 0.000000 -vn -0.964080 0.265542 0.000000 -vn -0.988372 0.151891 0.000000 -vn -0.888668 0.391644 -0.238441 -vn -0.907315 0.342753 -0.243446 -vn -0.931028 0.265877 -0.249855 -vn -0.954558 0.152104 -0.256172 -vn -0.785638 0.419416 -0.454756 -vn -0.796075 0.392285 -0.460799 -vn -0.812830 0.343333 -0.470504 -vn -0.834162 0.266366 -0.482864 -vn -0.855312 0.152379 -0.495132 -vn -0.964782 -0.045717 -0.258980 -vn -0.864498 -0.045808 -0.500504 -vn -0.641804 0.419691 -0.641804 -vn -0.650349 0.392499 -0.650349 -vn -0.664052 0.343577 -0.664052 -vn -0.681509 0.266579 -0.681509 -vn -0.698813 0.152501 -0.698813 -vn -0.706351 -0.045869 -0.706351 -vn -0.454756 0.419416 -0.785638 -vn -0.460799 0.392285 -0.796075 -vn -0.470504 0.343333 -0.812830 -vn -0.482864 0.266366 -0.834162 -vn -0.495132 0.152379 -0.855312 -vn -0.500504 -0.045808 -0.864498 -vn -0.235298 0.418744 -0.877041 -vn -0.238441 0.391644 -0.888668 -vn -0.243446 0.342753 -0.907315 -vn -0.249855 0.265908 -0.931028 -vn -0.256172 0.152104 -0.954558 -vn -0.258980 -0.045717 -0.964782 -vn 0.000000 0.418256 -0.908292 -vn 0.000000 0.391156 -0.920286 -vn 0.000000 0.342357 -0.939543 -vn 0.000000 0.265542 -0.964080 -vn 0.000000 0.151891 -0.988372 -vn 0.000000 -0.045656 -0.998932 -vn 0.235298 0.418744 -0.877041 -vn 0.238441 0.391644 -0.888668 -vn 0.243446 0.342753 -0.907315 -vn 0.249855 0.265877 -0.931028 -vn 0.256172 0.152104 -0.954558 -vn 0.258980 -0.045717 -0.964782 -vn 0.454756 0.419416 -0.785638 -vn 0.460799 0.392285 -0.796075 -vn 0.470504 0.343333 -0.812830 -vn 0.482864 0.266366 -0.834162 -vn 0.495132 0.152379 -0.855312 -vn 0.500504 -0.045808 -0.864498 -vn 0.641804 0.419691 -0.641804 -vn 0.650349 0.392499 -0.650349 -vn 0.664052 0.343577 -0.664052 -vn 0.681509 0.266579 -0.681509 -vn 0.698813 0.152501 -0.698813 -vn 0.706351 -0.045869 -0.706351 -vn 0.785638 0.419416 -0.454756 -vn 0.796075 0.392285 -0.460799 -vn 0.812830 0.343364 -0.470504 -vn 0.834162 0.266366 -0.482864 -vn 0.855312 0.152379 -0.495132 -vn 0.864498 -0.045808 -0.500504 -vn 0.877041 0.418744 -0.235298 -vn 0.888668 0.391644 -0.238441 -vn 0.907315 0.342753 -0.243446 -vn 0.795892 -0.566485 0.213538 -vn 0.712180 -0.701987 0.000000 -vn 0.687399 -0.702445 0.184393 -vn 0.652974 -0.757347 0.000000 -vn 0.630146 -0.757805 0.169012 -vn 0.724021 -0.689749 0.000000 -vn 0.698752 -0.690329 0.187414 -vn 0.886410 -0.462874 0.000000 -vn 0.855861 -0.463454 0.229530 -vn 0.817774 -0.327158 0.473434 -vn 0.712729 -0.567248 0.412549 -vn 0.615345 -0.703146 0.356151 -vn 0.564043 -0.758446 0.326456 -vn 0.625660 -0.690939 0.362102 -vn 0.766625 -0.464125 0.443678 -vn 0.668111 -0.327403 0.668111 -vn 0.582171 -0.567522 0.582171 -vn 0.502579 -0.703421 0.502579 -vn 0.460646 -0.758660 0.460646 -vn 0.510971 -0.691183 0.510971 -vn 0.626209 -0.464370 0.626240 -vn 0.473434 -0.327158 0.817774 -vn 0.412549 -0.567248 0.712729 -vn 0.356151 -0.703146 0.615375 -vn 0.326456 -0.758446 0.564043 -vn 0.362102 -0.690939 0.625660 -vn 0.443678 -0.464125 0.766625 -vn 0.245003 -0.326609 0.912839 -vn 0.213538 -0.566485 0.795892 -vn 0.184393 -0.702445 0.687399 -vn 0.169012 -0.757805 0.630146 -vn 0.187414 -0.690329 0.698752 -vn 0.229530 -0.463454 0.855831 -vn 0.000000 -0.326243 0.945250 -vn 0.000000 -0.565996 0.824396 -vn 0.000000 -0.701987 0.712180 -vn 0.000000 -0.757347 0.652974 -vn 0.000000 -0.689749 0.724021 -vn 0.000000 -0.462905 0.886380 -vn -0.245003 -0.326609 0.912839 -vn -0.213538 -0.566485 0.795892 -vn -0.184393 -0.702445 0.687399 -vn -0.169012 -0.757805 0.630146 -vn -0.187414 -0.690329 0.698752 -vn -0.229530 -0.463454 0.855861 -vn -0.473434 -0.327158 0.817774 -vn -0.412549 -0.567248 0.712729 -vn -0.356151 -0.703146 0.615375 -vn -0.326456 -0.758446 0.564043 -vn -0.362102 -0.690939 0.625660 -vn -0.443678 -0.464125 0.766625 -vn -0.668111 -0.327403 0.668111 -vn -0.582171 -0.567522 0.582171 -vn -0.502579 -0.703421 0.502579 -vn -0.460646 -0.758660 0.460646 -vn -0.510971 -0.691183 0.510971 -vn -0.626209 -0.464370 0.626209 -vn -0.817774 -0.327158 0.473434 -vn -0.712729 -0.567248 0.412549 -vn -0.615375 -0.703146 0.356151 -vn -0.564043 -0.758446 0.326456 -vn -0.625660 -0.690939 0.362102 -vn -0.766625 -0.464125 0.443678 -vn -0.912931 -0.326609 0.244575 -vn -0.687399 -0.702445 0.184393 -vn -0.630146 -0.757805 0.169012 -vn -0.698752 -0.690329 0.187414 -vn -0.855831 -0.463485 0.229530 -vn -0.824396 -0.565996 0.000000 -vn -0.712180 -0.701987 0.000000 -vn -0.652974 -0.757347 0.000000 -vn -0.724021 -0.689749 0.000000 -vn -0.886410 -0.462874 0.000000 -vn -0.795892 -0.566485 -0.213538 -vn -0.687399 -0.702445 -0.184393 -vn -0.630146 -0.757805 -0.169012 -vn -0.698752 -0.690329 -0.187414 -vn -0.855831 -0.463454 -0.229530 -vn -0.817774 -0.327158 -0.473434 -vn -0.712729 -0.567217 -0.412549 -vn -0.615375 -0.703146 -0.356151 -vn -0.564043 -0.758446 -0.326456 -vn -0.625660 -0.690939 -0.362102 -vn -0.766625 -0.464125 -0.443678 -vn -0.668111 -0.327403 -0.668111 -vn -0.582171 -0.567522 -0.582171 -vn -0.502579 -0.703421 -0.502579 -vn -0.460646 -0.758660 -0.460646 -vn -0.510971 -0.691183 -0.510971 -vn -0.626209 -0.464370 -0.626209 -vn -0.473434 -0.327158 -0.817774 -vn -0.412549 -0.567248 -0.712729 -vn -0.356151 -0.703146 -0.615375 -vn -0.326456 -0.758446 -0.564043 -vn -0.362102 -0.690939 -0.625660 -vn -0.443678 -0.464125 -0.766625 -vn -0.245003 -0.326609 -0.912839 -vn -0.213538 -0.566485 -0.795892 -vn -0.184393 -0.702445 -0.687399 -vn -0.169012 -0.757805 -0.630146 -vn -0.187414 -0.690329 -0.698752 -vn -0.229530 -0.463454 -0.855831 -vn 0.000000 -0.326243 -0.945250 -vn 0.000000 -0.565996 -0.824396 -vn 0.000000 -0.701987 -0.712180 -vn 0.000000 -0.757347 -0.652974 -vn 0.000000 -0.689749 -0.724021 -vn 0.000000 -0.462905 -0.886380 -vn 0.245003 -0.326609 -0.912839 -vn 0.213538 -0.566485 -0.795892 -vn 0.184393 -0.702445 -0.687399 -vn 0.169012 -0.757805 -0.630146 -vn 0.187445 -0.690329 -0.698752 -vn 0.229530 -0.463454 -0.855861 -vn 0.473434 -0.327158 -0.817774 -vn 0.412549 -0.567248 -0.712729 -vn 0.356151 -0.703146 -0.615375 -vn 0.326456 -0.758446 -0.564043 -vn 0.362102 -0.690939 -0.625660 -vn 0.443678 -0.464125 -0.766625 -vn 0.668111 -0.327403 -0.668111 -vn 0.582171 -0.567522 -0.582171 -vn 0.502579 -0.703421 -0.502579 -vn 0.460646 -0.758660 -0.460646 -vn 0.510971 -0.691183 -0.510971 -vn 0.626209 -0.464370 -0.626209 -vn 0.817774 -0.327158 -0.473434 -vn 0.712729 -0.567248 -0.412549 -vn 0.615375 -0.703146 -0.356151 -vn 0.564043 -0.758446 -0.326456 -vn 0.625660 -0.690939 -0.362102 -vn 0.766625 -0.464125 -0.443678 -vn 0.912839 -0.326609 -0.244942 -vn 0.795892 -0.566485 -0.213538 -vn 0.687399 -0.702445 -0.184393 -vn 0.630146 -0.757805 -0.169012 -vn 0.698752 -0.690329 -0.187414 -vn 0.855861 -0.463454 -0.229530 -vn 0.025666 -0.999664 0.000000 -vn 0.000000 -1.000000 0.000000 -vn 0.024781 -0.999664 -0.006623 -vn 0.068667 -0.997620 0.000000 -vn 0.066256 -0.997620 -0.017731 -vn 0.157170 -0.987548 0.000000 -vn 0.151677 -0.987579 -0.040620 -vn 0.373150 -0.927763 0.000000 -vn 0.360118 -0.927885 -0.096469 -vn 0.789148 -0.614154 0.000000 -vn 0.762017 -0.614399 -0.204505 -vn 0.022156 -0.999664 -0.012787 -vn 0.059236 -0.997650 -0.034272 -vn 0.135624 -0.987640 -0.078463 -vn 0.322153 -0.928129 -0.186377 -vn 0.682333 -0.615131 -0.394971 -vn 0.018067 -0.999664 -0.018067 -vn 0.048341 -0.997650 -0.048341 -vn 0.110691 -0.987640 -0.110691 -vn 0.262947 -0.928251 -0.262947 -vn 0.557329 -0.615375 -0.557329 -vn 0.012787 -0.999664 -0.022156 -vn 0.034272 -0.997650 -0.059236 -vn 0.078463 -0.987640 -0.135624 -vn 0.186377 -0.928129 -0.322153 -vn 0.394971 -0.615131 -0.682302 -vn 0.006623 -0.999664 -0.024781 -vn 0.017731 -0.997620 -0.066256 -vn 0.040620 -0.987579 -0.151677 -vn 0.096469 -0.927885 -0.360118 -vn 0.204474 -0.614399 -0.762017 -vn 0.000000 -0.999664 -0.025666 -vn 0.000000 -0.997620 -0.068667 -vn 0.000000 -0.987548 -0.157170 -vn 0.000000 -0.927763 -0.373150 -vn 0.000000 -0.614154 -0.789148 -vn -0.006623 -0.999664 -0.024781 -vn -0.017731 -0.997620 -0.066256 -vn -0.040620 -0.987579 -0.151677 -vn -0.096469 -0.927885 -0.360118 -vn -0.204474 -0.614399 -0.762017 -vn -0.012787 -0.999664 -0.022156 -vn -0.034272 -0.997650 -0.059236 -vn -0.078463 -0.987640 -0.135624 -vn -0.186377 -0.928129 -0.322153 -vn -0.394971 -0.615131 -0.682333 -vn -0.018067 -0.999664 -0.018067 -vn -0.048341 -0.997650 -0.048341 -vn -0.110691 -0.987640 -0.110691 -vn -0.262947 -0.928251 -0.262947 -vn -0.557329 -0.615375 -0.557329 -vn -0.022156 -0.999664 -0.012787 -vn -0.059236 -0.997650 -0.034272 -vn -0.135624 -0.987640 -0.078463 -vn -0.322153 -0.928129 -0.186377 -vn -0.682302 -0.615131 -0.394971 -vn -0.024781 -0.999664 -0.006623 -vn -0.066256 -0.997620 -0.017731 -vn -0.151677 -0.987579 -0.040620 -vn -0.360118 -0.927885 -0.096469 -vn -0.762017 -0.614399 -0.204474 -vn -0.025666 -0.999664 0.000000 -vn -0.068667 -0.997620 0.000000 -vn -0.157170 -0.987548 0.000000 -vn -0.373150 -0.927763 0.000000 -vn -0.789148 -0.614154 0.000000 -vn -0.024781 -0.999664 0.006623 -vn -0.066256 -0.997620 0.017731 -vn -0.151677 -0.987579 0.040620 -vn -0.360149 -0.927885 0.096469 -vn -0.762017 -0.614399 0.204474 -vn -0.022156 -0.999664 0.012787 -vn -0.059236 -0.997650 0.034272 -vn -0.135624 -0.987640 0.078463 -vn -0.322153 -0.928129 0.186377 -vn -0.682333 -0.615131 0.394971 -vn -0.018067 -0.999664 0.018067 -vn -0.048341 -0.997650 0.048341 -vn -0.110691 -0.987640 0.110691 -vn -0.262947 -0.928251 0.262947 -vn -0.557329 -0.615375 0.557329 -vn -0.012787 -0.999664 0.022156 -vn -0.034272 -0.997650 0.059236 -vn -0.078463 -0.987640 0.135624 -vn -0.186377 -0.928129 0.322153 -vn -0.394971 -0.615131 0.682302 -vn -0.006623 -0.999664 0.024781 -vn -0.017731 -0.997620 0.066256 -vn -0.040620 -0.987579 0.151677 -vn -0.096469 -0.927885 0.360118 -vn -0.204474 -0.614399 0.762017 -vn 0.000000 -0.999664 0.025666 -vn 0.000000 -0.997620 0.068667 -vn 0.000000 -0.987548 0.157170 -vn 0.000000 -0.927763 0.373150 -vn 0.000000 -0.614154 0.789148 -vn 0.006623 -0.999664 0.024781 -vn 0.017731 -0.997620 0.066256 -vn 0.040620 -0.987579 0.151677 -vn 0.096469 -0.927885 0.360149 -vn 0.204474 -0.614399 0.762017 -vn 0.012787 -0.999664 0.022156 -vn 0.034272 -0.997650 0.059236 -vn 0.078463 -0.987640 0.135624 -vn 0.186377 -0.928129 0.322153 -vn 0.394971 -0.615131 0.682333 -vn 0.018067 -0.999664 0.018067 -vn 0.048341 -0.997650 0.048341 -vn 0.110691 -0.987640 0.110691 -vn 0.262947 -0.928251 0.262947 -vn 0.557329 -0.615375 0.557329 -vn 0.022156 -0.999664 0.012787 -vn 0.059236 -0.997650 0.034272 -vn 0.135624 -0.987640 0.078463 -vn 0.322153 -0.928129 0.186346 -vn 0.682302 -0.615131 0.394971 -vn 0.024781 -0.999664 0.006623 -vn 0.066256 -0.997620 0.017731 -vn 0.151677 -0.987579 0.040620 -vn 0.360118 -0.927885 0.096469 -vn 0.762017 -0.614399 0.204474 -vn 0.464827 -0.373638 -0.802667 -vn 0.655812 -0.373882 -0.655812 -vn 0.000000 -0.372539 0.927976 -vn -0.240699 -0.373028 0.896023 -vn -0.802667 -0.373608 0.464827 -vn 0.896023 -0.372997 -0.240699 -vn -0.927976 -0.372539 0.000000 -vn 0.927976 -0.372539 0.000000 -vn 0.717063 0.696982 0.000000 -vn 0.990387 -0.138310 -0.000061 -vn 0.956694 -0.138737 0.255806 -vn 0.692129 0.697470 0.185583 -vn 0.857326 -0.139317 0.495529 -vn 0.620045 0.697653 0.358837 -vn 0.700125 -0.139531 0.700217 -vn 0.506516 0.697714 0.506546 -vn 0.495468 -0.139286 0.857356 -vn 0.358776 0.697714 0.620014 -vn 0.255867 -0.138737 0.956694 -vn 0.185583 0.697531 0.692068 -vn 0.000061 -0.138310 0.990387 -vn 0.000000 0.696982 0.717063 -vn -0.255806 -0.138737 0.956694 -vn -0.185583 0.697470 0.692129 -vn -0.495529 -0.139317 0.857326 -vn -0.358837 0.697653 0.620045 -vn -0.700217 -0.139531 0.700156 -vn -0.506546 0.697714 0.506516 -vn -0.857356 -0.139286 0.495468 -vn -0.620014 0.697714 0.358776 -vn -0.956694 -0.138737 0.255867 -vn -0.692068 0.697531 0.185583 -vn -0.990387 -0.138310 0.000061 -vn -0.717063 0.696982 0.000000 -vn -0.956694 -0.138737 -0.255806 -vn -0.692129 0.697470 -0.185583 -vn -0.857326 -0.139317 -0.495529 -vn -0.620045 0.697653 -0.358837 -vn -0.700125 -0.139531 -0.700217 -vn -0.506516 0.697714 -0.506546 -vn -0.495468 -0.139286 -0.857356 -vn -0.358776 0.697714 -0.620014 -vn -0.255867 -0.138737 -0.956694 -vn -0.185583 0.697531 -0.692068 -vn -0.000061 -0.138310 -0.990387 -vn 0.000000 0.696982 -0.717063 -vn 0.255806 -0.138737 -0.956694 -vn 0.185583 0.697470 -0.692129 -vn 0.495529 -0.139317 -0.857326 -vn 0.358837 0.697653 -0.620045 -vn 0.700217 -0.139531 -0.700156 -vn 0.506546 0.697714 -0.506516 -vn 0.857356 -0.139286 -0.495468 -vn 0.620014 0.697714 -0.358776 -vn 0.956694 -0.138737 -0.255867 -vn 0.692068 0.697531 -0.185583 -vn 0.292520 0.956236 0.000000 -vn 0.282083 0.956389 0.075686 -vn 0.177953 0.984008 0.000000 -vn 0.171606 0.984069 0.046022 -vn 0.158879 0.987274 0.000000 -vn 0.153264 0.987304 0.041078 -vn 0.217719 0.975982 0.000000 -vn 0.210059 0.976043 0.056276 -vn 0.504715 0.863277 0.000000 -vn 0.487197 0.863460 0.130558 -vn 0.693258 0.720664 0.000000 -vn 0.669057 0.721183 0.179449 -vn 0.252388 0.956511 0.146092 -vn 0.153508 0.984130 0.088839 -vn 0.137059 0.987365 0.079318 -vn 0.187872 0.976135 0.108676 -vn 0.435926 0.863887 0.252205 -vn 0.598956 0.721824 0.346660 -vn 0.206091 0.956572 0.206091 -vn 0.125340 0.984161 0.125340 -vn 0.111911 0.987396 0.111911 -vn 0.153356 0.976196 0.153356 -vn 0.355907 0.864071 0.355907 -vn 0.489151 0.722098 0.489151 -vn 0.146092 0.956511 0.252388 -vn 0.088839 0.984130 0.153508 -vn 0.079318 0.987365 0.137059 -vn 0.108676 0.976135 0.187872 -vn 0.252205 0.863887 0.435926 -vn 0.346660 0.721824 0.598956 -vn 0.075686 0.956389 0.282083 -vn 0.046022 0.984069 0.171606 -vn 0.041078 0.987304 0.153264 -vn 0.056276 0.976043 0.210059 -vn 0.130558 0.863460 0.487197 -vn 0.179449 0.721183 0.669057 -vn 0.000000 0.956236 0.292520 -vn 0.000000 0.984008 0.177953 -vn 0.000000 0.987274 0.158879 -vn 0.000000 0.975982 0.217719 -vn 0.000000 0.863277 0.504715 -vn 0.000000 0.720664 0.693258 -vn -0.075686 0.956389 0.282083 -vn -0.046022 0.984069 0.171606 -vn -0.041078 0.987304 0.153264 -vn -0.056276 0.976043 0.210059 -vn -0.130558 0.863460 0.487197 -vn -0.179449 0.721183 0.669057 -vn -0.146092 0.956511 0.252388 -vn -0.088839 0.984130 0.153508 -vn -0.079318 0.987365 0.137059 -vn -0.108676 0.976135 0.187872 -vn -0.252205 0.863887 0.435926 -vn -0.346660 0.721824 0.598956 -vn -0.206091 0.956572 0.206091 -vn -0.125340 0.984161 0.125340 -vn -0.111911 0.987396 0.111911 -vn -0.153356 0.976196 0.153356 -vn -0.355907 0.864071 0.355907 -vn -0.489151 0.722098 0.489151 -vn -0.252388 0.956511 0.146092 -vn -0.153508 0.984130 0.088839 -vn -0.137059 0.987365 0.079318 -vn -0.187872 0.976135 0.108676 -vn -0.435926 0.863887 0.252205 -vn -0.598956 0.721824 0.346660 -vn -0.282083 0.956389 0.075686 -vn -0.171606 0.984069 0.046022 -vn -0.153264 0.987304 0.041078 -vn -0.210059 0.976043 0.056276 -vn -0.487197 0.863460 0.130558 -vn -0.669057 0.721183 0.179449 -vn -0.292520 0.956236 0.000000 -vn -0.177953 0.984008 0.000000 -vn -0.158879 0.987274 0.000000 -vn -0.217719 0.975982 0.000000 -vn -0.504715 0.863277 0.000000 -vn -0.693258 0.720664 0.000000 -vn -0.282083 0.956389 -0.075686 -vn -0.171606 0.984069 -0.046022 -vn -0.153264 0.987304 -0.041078 -vn -0.210059 0.976043 -0.056276 -vn -0.487197 0.863460 -0.130558 -vn -0.669057 0.721183 -0.179449 -vn -0.252388 0.956511 -0.146092 -vn -0.153508 0.984130 -0.088839 -vn -0.137059 0.987365 -0.079318 -vn -0.187872 0.976135 -0.108676 -vn -0.435926 0.863887 -0.252205 -vn -0.598956 0.721824 -0.346660 -vn -0.206091 0.956572 -0.206091 -vn -0.125340 0.984161 -0.125340 -vn -0.111911 0.987396 -0.111911 -vn -0.153356 0.976196 -0.153356 -vn -0.355907 0.864071 -0.355907 -vn -0.489151 0.722098 -0.489151 -vn -0.146092 0.956511 -0.252388 -vn -0.088839 0.984130 -0.153508 -vn -0.079318 0.987365 -0.137059 -vn -0.108676 0.976135 -0.187872 -vn -0.252205 0.863887 -0.435926 -vn -0.346660 0.721824 -0.598956 -vn -0.075686 0.956389 -0.282083 -vn -0.046022 0.984069 -0.171606 -vn -0.041078 0.987304 -0.153264 -vn -0.056276 0.976043 -0.210059 -vn -0.130558 0.863460 -0.487197 -vn -0.179449 0.721183 -0.669057 -vn 0.000000 0.956236 -0.292520 -vn 0.000000 0.984008 -0.177953 -vn 0.000000 0.987274 -0.158879 -vn 0.000000 0.975982 -0.217719 -vn 0.000000 0.863277 -0.504715 -vn 0.000000 0.720664 -0.693258 -vn 0.075686 0.956389 -0.282083 -vn 0.046022 0.984069 -0.171606 -vn 0.041078 0.987304 -0.153264 -vn 0.056276 0.976043 -0.210059 -vn 0.130558 0.863460 -0.487197 -vn 0.179449 0.721183 -0.669057 -vn 0.146092 0.956511 -0.252388 -vn 0.088839 0.984130 -0.153508 -vn 0.079318 0.987365 -0.137059 -vn 0.108676 0.976135 -0.187872 -vn 0.252205 0.863887 -0.435926 -vn 0.346660 0.721824 -0.598956 -vn 0.206091 0.956572 -0.206091 -vn 0.125340 0.984161 -0.125340 -vn 0.111911 0.987396 -0.111911 -vn 0.153356 0.976196 -0.153356 -vn 0.355907 0.864071 -0.355907 -vn 0.489151 0.722098 -0.489151 -vn 0.252388 0.956511 -0.146092 -vn 0.153508 0.984130 -0.088839 -vn 0.137059 0.987365 -0.079318 -vn 0.187872 0.976135 -0.108676 -vn 0.435926 0.863887 -0.252205 -vn 0.598956 0.721824 -0.346660 -vn 0.282083 0.956389 -0.075686 -vn 0.171606 0.984069 -0.046022 -vn 0.153264 0.987304 -0.041078 -vn 0.210059 0.976043 -0.056276 -vn 0.487197 0.863460 -0.130558 -vn 0.669057 0.721183 -0.179449 -vn 0.363842 0.931455 0.000000 -vn 0.000000 1.000000 0.000000 -vn 0.351451 0.931516 0.093509 -vn 0.968261 0.249916 0.000000 -vn 0.935423 0.249763 0.250130 -vn 0.842860 -0.538102 0.000000 -vn 0.813959 -0.538713 0.217292 -vn 0.786767 -0.617206 -0.000031 -vn 0.759514 -0.618000 0.202857 -vn 0.314432 0.931791 0.181280 -vn 0.838404 0.249855 0.484359 -vn 0.729026 -0.539720 0.420911 -vn 0.680013 -0.619068 0.392743 -vn 0.256386 0.931913 0.256417 -vn 0.684652 0.249886 0.684652 -vn 0.595050 -0.540147 0.595050 -vn 0.555010 -0.619526 0.555040 -vn 0.181280 0.931791 0.314432 -vn 0.484359 0.249825 0.838404 -vn 0.420881 -0.539720 0.729026 -vn 0.392712 -0.619098 0.680013 -vn 0.093509 0.931516 0.351451 -vn 0.250160 0.249763 0.935423 -vn 0.217322 -0.538713 0.813959 -vn 0.202887 -0.618030 0.759484 -vn 0.000000 0.931455 0.363842 -vn 0.000000 0.249916 0.968261 -vn 0.000000 -0.538102 0.842860 -vn 0.000031 -0.617206 0.786767 -vn -0.093509 0.931516 0.351451 -vn -0.250130 0.249763 0.935423 -vn -0.217292 -0.538682 0.813959 -vn -0.202857 -0.618000 0.759514 -vn -0.181280 0.931791 0.314432 -vn -0.484359 0.249855 0.838404 -vn -0.420911 -0.539720 0.729026 -vn -0.392743 -0.619068 0.680013 -vn -0.256417 0.931913 0.256386 -vn -0.684652 0.249886 0.684652 -vn -0.595050 -0.540147 0.595050 -vn -0.555040 -0.619526 0.555010 -vn -0.314432 0.931791 0.181280 -vn -0.838404 0.249825 0.484359 -vn -0.729026 -0.539720 0.420881 -vn -0.680013 -0.619098 0.392712 -vn -0.351451 0.931516 0.093509 -vn -0.935423 0.249763 0.250160 -vn -0.813959 -0.538713 0.217292 -vn -0.759484 -0.618030 0.202887 -vn -0.363842 0.931455 0.000000 -vn -0.968261 0.249916 0.000000 -vn -0.842860 -0.538102 0.000000 -vn -0.786767 -0.617206 0.000031 -vn -0.351451 0.931516 -0.093509 -vn -0.935423 0.249763 -0.250130 -vn -0.813959 -0.538713 -0.217292 -vn -0.759514 -0.618000 -0.202857 -vn -0.314432 0.931791 -0.181280 -vn -0.838404 0.249855 -0.484359 -vn -0.729026 -0.539720 -0.420911 -vn -0.680013 -0.619068 -0.392743 -vn -0.256386 0.931913 -0.256417 -vn -0.684652 0.249886 -0.684652 -vn -0.595050 -0.540147 -0.595050 -vn -0.555010 -0.619526 -0.555040 -vn -0.181280 0.931791 -0.314432 -vn -0.484359 0.249825 -0.838404 -vn -0.420881 -0.539720 -0.729026 -vn -0.392712 -0.619098 -0.680013 -vn -0.093509 0.931516 -0.351451 -vn -0.250160 0.249763 -0.935423 -vn -0.217322 -0.538713 -0.813959 -vn -0.202887 -0.618030 -0.759484 -vn 0.000000 0.931455 -0.363842 -vn 0.000000 0.249916 -0.968261 -vn 0.000000 -0.538102 -0.842860 -vn -0.000031 -0.617206 -0.786767 -vn 0.093509 0.931516 -0.351451 -vn 0.250130 0.249763 -0.935423 -vn 0.217292 -0.538682 -0.813959 -vn 0.202857 -0.618000 -0.759514 -vn 0.181280 0.931791 -0.314432 -vn 0.484359 0.249855 -0.838404 -vn 0.420911 -0.539720 -0.729026 -vn 0.392743 -0.619068 -0.680013 -vn 0.256417 0.931913 -0.256386 -vn 0.684652 0.249886 -0.684652 -vn 0.595050 -0.540147 -0.595050 -vn 0.555040 -0.619526 -0.555010 -vn 0.314432 0.931791 -0.181280 -vn 0.838404 0.249825 -0.484359 -vn 0.729026 -0.539720 -0.420881 -vn 0.680013 -0.619098 -0.392712 -vn 0.351451 0.931516 -0.093509 -vn 0.935423 0.249763 -0.250160 -vn 0.813959 -0.538713 -0.217292 -vn 0.759484 -0.618030 -0.202887 -vn -0.354198 0.930296 -0.095187 -vn 0.095187 0.930296 0.354198 -vn 0.354198 0.930296 0.095187 -vn 0.183721 0.930387 0.317179 -vn -0.183721 0.930387 -0.317179 -vn -0.367443 0.930021 0.000000 -vn -0.183721 0.930387 0.317179 -vn 0.367412 0.930021 0.000000 -vn -0.317179 0.930387 0.183721 -vn -0.095187 0.930296 -0.354198 -vn 0.000000 0.930021 -0.367443 -vn -0.354198 0.930296 0.095187 -vn 0.095187 0.930296 -0.354198 -vn 0.000000 0.930021 0.367443 -vn -0.317179 0.930387 -0.183721 -vn 0.317179 0.930387 -0.183721 -vn -0.095187 0.930296 0.354198 -vn 0.317179 0.930387 0.183721 -vn 0.354198 0.930296 -0.095187 -vn 0.183721 0.930387 -0.317179 -vn -0.034730 0.999390 0.000000 -vn 0.033479 0.999390 -0.009003 -vn 0.034730 0.999390 0.000000 -vn 0.009003 0.999390 -0.033479 -vn 0.000000 0.999390 -0.034730 -vn -0.259163 0.930387 0.259163 -vn -0.017335 0.999390 0.029939 -vn 0.947539 0.295083 0.122654 -vn -0.004486 0.999969 0.000000 -vn -0.004151 0.950468 0.310739 -vn -0.003021 0.719291 0.694662 -vn -0.998688 0.050722 0.000000 -vn -0.003143 0.719321 -0.694632 -vn 0.970214 0.213324 -0.114505 -vn -0.136235 0.879482 -0.455947 -vn 0.949858 0.312662 0.000000 -vn 0.055757 -0.017579 -0.998260 -vn 0.201300 -0.540880 -0.816645 -vn 0.974456 -0.186071 -0.125645 -vn 0.988098 -0.096286 0.119938 -vn 0.974456 -0.186041 0.125614 -vn -0.879574 -0.475723 0.000000 -vn -0.873775 -0.472610 -0.114475 -vn 0.988067 -0.096286 -0.119938 -vn 0.295480 -0.855464 -0.425214 -vn -0.976196 -0.174993 -0.127903 -vn -0.971007 -0.205725 0.121677 -vn -0.976196 -0.174963 0.127903 -vn -0.976196 -0.174993 0.127903 -vn 0.896054 -0.372997 0.240699 -vn -0.802667 -0.373638 -0.464827 -vn -0.655812 -0.373852 -0.655812 -vn -0.240699 -0.373028 -0.896023 -vn -0.464827 -0.373638 -0.802667 -vn -0.896023 -0.373028 0.240699 -vn 0.000000 -0.372539 -0.927976 -vn -0.655812 -0.373852 0.655812 -vn 0.240699 -0.373028 0.896023 -vn -0.896023 -0.373028 -0.240699 -vn 0.802667 -0.373638 -0.464827 -vn 0.655812 -0.373882 0.655812 -vn 0.464827 -0.373638 0.802667 -vn -0.464827 -0.373638 0.802667 -vn 0.240699 -0.373028 -0.896023 -vn 0.802667 -0.373638 0.464827 -vn -0.033479 0.999390 0.009003 -vn -0.259163 0.930387 -0.259163 -vn -0.024445 0.999390 -0.024445 -vn -0.029939 0.999390 -0.017335 -vn 0.259163 0.930387 -0.259163 -vn 0.024445 0.999390 -0.024445 -vn 0.017335 0.999390 -0.029939 -vn 0.029939 0.999390 -0.017335 -vn 0.033479 0.999390 0.009003 -vn -0.009003 0.999390 0.033479 -vn 0.000000 0.999390 0.034730 -vn -0.017335 0.999390 -0.029939 -vn 0.259163 0.930387 0.259163 -vn 0.029939 0.999390 0.017335 -vn -0.009003 0.999390 -0.033479 -vn 0.024445 0.999390 0.024445 -vn 0.017335 0.999390 0.029939 -vn 0.009003 0.999390 0.033479 -vn -0.033479 0.999390 -0.009003 -vn -0.024445 0.999390 0.024445 -vn -0.029939 0.999390 0.017335 -vn 0.055757 -0.017579 0.998260 -vn 0.294198 -0.855403 0.426252 -vn 0.201300 -0.540880 0.816614 -s 1 -f 34//1 1243//2 593//3 -f 52//4 27//5 40//6 -f 52//4 40//6 65//7 -f 77//8 52//4 65//7 -f 77//8 65//7 84//9 -f 107//10 77//8 84//9 -f 107//10 84//9 85//11 -f 115//12 107//10 85//11 -f 115//12 85//11 99//13 -f 129//14 115//12 99//13 -f 129//14 99//13 128//15 -f 1252//16 36//17 40//6 -f 65//7 40//6 64//18 -f 65//7 64//18 58//19 -f 84//9 65//7 58//19 -f 84//9 58//19 59//20 -f 85//11 84//9 59//20 -f 85//11 59//20 70//21 -f 99//13 85//11 70//21 -f 99//13 70//21 98//22 -f 128//15 99//13 98//22 -f 128//15 98//22 114//23 -f 1244//24 33//25 64//18 -f 58//19 64//18 33//25 -f 58//19 33//25 35//26 -f 59//20 58//19 35//26 -f 59//20 35//26 45//27 -f 70//21 59//20 45//27 -f 70//21 45//27 69//28 -f 98//22 70//21 69//28 -f 98//22 69//28 83//29 -f 114//23 98//22 83//29 -f 114//23 83//29 113//30 -f 553//31 1//32 566//33 -f 35//26 33//25 20//34 -f 35//26 20//34 24//35 -f 45//27 35//26 24//35 -f 45//27 24//35 44//36 -f 69//28 45//27 44//36 -f 69//28 44//36 57//37 -f 83//29 69//28 57//37 -f 83//29 57//37 82//38 -f 113//30 83//29 82//38 -f 113//30 82//38 112//39 -f 566//33 1283//40 9//41 -f 24//35 20//34 18//42 -f 24//35 18//42 23//43 -f 44//36 24//35 23//43 -f 44//36 23//43 32//44 -f 57//37 44//36 32//44 -f 57//37 32//44 56//45 -f 82//38 57//37 56//45 -f 82//38 56//45 81//46 -f 112//39 82//38 81//46 -f 112//39 81//46 111//47 -f 4//48 8//49 1250//50 -f 23//43 18//42 8//49 -f 23//43 8//49 17//51 -f 32//44 23//43 17//51 -f 32//44 17//51 31//52 -f 56//45 32//44 31//52 -f 56//45 31//52 55//53 -f 81//46 56//45 55//53 -f 81//46 55//53 80//54 -f 111//47 81//46 80//54 -f 111//47 80//54 110//55 -f 565//56 592//57 1233//58 -f 17//51 8//49 4//48 -f 17//51 4//48 16//59 -f 31//52 17//51 16//59 -f 31//52 16//59 30//60 -f 55//53 31//52 30//60 -f 55//53 30//60 54//61 -f 80//54 55//53 54//61 -f 80//54 54//61 79//62 -f 110//55 80//54 79//62 -f 110//55 79//62 109//63 -f 1//32 565//56 2//64 -f 6//65 2//64 565//56 -f 16//59 4//48 7//66 -f 16//59 7//66 22//67 -f 30//60 16//59 22//67 -f 30//60 22//67 43//68 -f 54//61 30//60 43//68 -f 54//61 43//68 68//69 -f 79//62 54//61 68//69 -f 79//62 68//69 97//70 -f 109//63 79//62 97//70 -f 109//63 97//70 127//71 -f 565//56 1233//58 1249//58 -f 13//72 14//73 5//74 -f 22//67 7//66 15//75 -f 22//67 15//75 29//76 -f 43//68 22//67 29//76 -f 43//68 29//76 42//77 -f 68//69 43//68 42//77 -f 68//69 42//77 67//78 -f 97//70 68//69 67//78 -f 97//70 67//78 96//79 -f 127//71 97//70 96//79 -f 127//71 96//79 126//80 -f 11//81 15//75 1240//82 -f 1289//83 34//1 593//3 -f 29//76 15//75 14//73 -f 29//76 14//73 21//84 -f 42//77 29//76 21//84 -f 42//77 21//84 41//85 -f 67//78 42//77 41//85 -f 67//78 41//85 66//86 -f 96//79 67//78 66//86 -f 96//79 66//86 95//87 -f 126//80 96//79 95//87 -f 126//80 95//87 125//88 -f 636//89 1243//2 26//2 -f 636//89 26//2 592//57 -f 21//84 14//73 13//72 -f 21//84 13//72 28//90 -f 41//85 21//84 28//90 -f 41//85 28//90 53//91 -f 66//86 41//85 53//91 -f 66//86 53//91 78//92 -f 95//87 66//86 78//92 -f 95//87 78//92 108//93 -f 125//88 95//87 108//93 -f 125//88 108//93 136//94 -f 12//95 13//72 5//74 -f 28//90 13//72 27//5 -f 28//90 27//5 52//4 -f 53//91 28//90 52//4 -f 53//91 52//4 77//8 -f 78//92 53//91 77//8 -f 78//92 77//8 107//10 -f 108//93 78//92 107//10 -f 108//93 107//10 115//12 -f 136//94 108//93 115//12 -f 136//94 115//12 129//14 -f 148//96 129//14 128//15 -f 148//96 128//15 143//97 -f 121//98 148//96 143//97 -f 121//98 143//97 122//99 -f 92//100 121//98 122//99 -f 92//100 122//99 93//101 -f 62//102 92//100 93//101 -f 62//102 93//101 63//103 -f 39//104 62//102 63//103 -f 39//104 63//103 51//105 -f 10//106 39//104 51//105 -f 10//106 51//105 49//107 -f 143//97 128//15 114//23 -f 143//97 114//23 142//108 -f 122//99 143//97 142//108 -f 122//99 142//108 123//109 -f 93//101 122//99 123//109 -f 93//101 123//109 94//110 -f 63//103 93//101 94//110 -f 63//103 94//110 76//111 -f 51//105 63//103 76//111 -f 51//105 76//111 75//112 -f 49//107 51//105 75//112 -f 49//107 75//112 61//113 -f 142//108 114//23 113//30 -f 142//108 113//30 141//114 -f 123//109 142//108 141//114 -f 123//109 141//114 124//115 -f 94//110 123//109 124//115 -f 94//110 124//115 106//116 -f 76//111 94//110 106//116 -f 76//111 106//116 105//117 -f 75//112 76//111 105//117 -f 75//112 105//117 91//118 -f 61//113 75//112 91//118 -f 61//113 91//118 90//119 -f 141//114 113//30 112//39 -f 141//114 112//39 140//120 -f 124//115 141//114 140//120 -f 124//115 140//120 135//121 -f 106//116 124//115 135//121 -f 106//116 135//121 134//122 -f 105//117 106//116 134//122 -f 105//117 134//122 120//123 -f 91//118 105//117 120//123 -f 91//118 120//123 119//124 -f 90//119 91//118 119//124 -f 140//120 112//39 111//47 -f 140//120 111//47 139//125 -f 135//121 140//120 139//125 -f 135//121 139//125 155//126 -f 134//122 135//121 155//126 -f 134//122 155//126 147//127 -f 120//123 134//122 147//127 -f 120//123 147//127 144//128 -f 119//124 120//123 144//128 -f 119//124 144//128 116//129 -f 26//2 1234//130 592//57 -f 139//125 111//47 110//55 -f 139//125 110//55 138//131 -f 155//126 139//125 138//131 -f 155//126 138//131 156//132 -f 147//127 155//126 156//132 -f 147//127 156//132 145//133 -f 144//128 147//127 145//133 -f 144//128 145//133 117//134 -f 116//129 144//128 117//134 -f 116//129 117//134 88//135 -f 27//5 13//72 12//95 -f 1242//136 88//135 71//137 -f 138//131 110//55 109//63 -f 138//131 109//63 137//138 -f 156//132 138//131 137//138 -f 156//132 137//138 154//139 -f 145//133 156//132 154//139 -f 145//133 154//139 131//140 -f 117//134 145//133 131//140 -f 117//134 131//140 101//141 -f 88//135 117//134 101//141 -f 88//135 101//141 86//142 -f 60//143 74//144 1248//145 -f 137//138 109//63 127//71 -f 137//138 127//71 151//146 -f 154//139 137//138 151//146 -f 154//139 151//146 153//147 -f 131//140 154//139 153//147 -f 131//140 153//147 130//148 -f 101//141 131//140 130//148 -f 101//141 130//148 100//149 -f 86//142 101//141 100//149 -f 86//142 100//149 74//144 -f 47//150 1235//151 631//152 -f 74//144 48//153 1248//145 -f 151//146 127//71 126//80 -f 151//146 126//80 150//154 -f 153//147 151//146 150//154 -f 153//147 150//154 146//155 -f 130//148 153//147 146//155 -f 130//148 146//155 118//156 -f 100//149 130//148 118//156 -f 100//149 118//156 89//157 -f 74//144 100//149 89//157 -f 74//144 89//157 48//153 -f 1247//158 87//159 715//160 -f 104//161 116//129 1242//136 -f 150//154 126//80 125//88 -f 150//154 125//88 149//162 -f 146//155 150//154 149//162 -f 146//155 149//162 132//163 -f 118//156 146//155 132//163 -f 118//156 132//163 102//164 -f 89//157 118//156 102//164 -f 89//157 102//164 72//165 -f 48//153 89//157 72//165 -f 48//153 72//165 46//166 -f 37//167 48//153 46//166 -f 37//167 46//166 38//168 -f 149//162 125//88 136//94 -f 149//162 136//94 152//169 -f 132//163 149//162 152//169 -f 132//163 152//169 133//170 -f 102//164 132//163 133//170 -f 102//164 133//170 103//171 -f 72//165 102//164 103//171 -f 72//165 103//171 73//172 -f 46//166 72//165 73//172 -f 46//166 73//172 50//173 -f 38//168 46//166 50//173 -f 38//168 50//173 25//174 -f 152//169 136//94 129//14 -f 152//169 129//14 148//96 -f 133//170 152//169 148//96 -f 133//170 148//96 121//98 -f 103//171 133//170 121//98 -f 103//171 121//98 92//100 -f 73//172 103//171 92//100 -f 73//172 92//100 62//102 -f 50//173 73//172 62//102 -f 50//173 62//102 39//104 -f 25//174 50//173 39//104 -f 39//104 10//106 25//174 -f 539//175 509//176 159//177 -f 160//178 158//179 157//180 -f 166//181 160//178 163//182 -f 166//181 163//182 173//183 -f 177//184 166//181 173//183 -f 177//184 173//183 183//185 -f 189//186 177//184 183//185 -f 189//186 183//185 197//187 -f 205//188 189//186 197//187 -f 205//188 197//187 216//189 -f 225//190 205//188 216//189 -f 225//190 216//189 235//191 -f 173//183 163//182 172//192 -f 173//183 172//192 184//193 -f 183//185 173//183 184//193 -f 183//185 184//193 198//194 -f 197//187 183//185 198//194 -f 197//187 198//194 217//195 -f 216//189 197//187 217//195 -f 216//189 217//195 236//196 -f 235//191 216//189 236//196 -f 235//191 236//196 253//197 -f 182//198 172//192 1254//199 -f 1255//200 167//201 595//202 -f 184//193 172//192 182//198 -f 184//193 182//198 196//203 -f 198//194 184//193 196//203 -f 198//194 196//203 214//204 -f 217//195 198//194 214//204 -f 217//195 214//204 232//205 -f 236//196 217//195 232//205 -f 236//196 232//205 250//206 -f 253//197 236//196 250//206 -f 253//197 250//206 267//207 -f 196//203 182//198 199//208 -f 196//203 199//208 215//209 -f 214//204 196//203 215//209 -f 214//204 215//209 233//210 -f 232//205 214//204 233//210 -f 232//205 233//210 251//211 -f 250//206 232//205 251//211 -f 250//206 251//211 257//212 -f 267//207 250//206 257//212 -f 267//207 257//212 256//213 -f 215//209 199//208 218//214 -f 215//209 218//214 234//215 -f 233//210 215//209 234//215 -f 233//210 234//215 241//216 -f 251//211 233//210 241//216 -f 251//211 241//216 240//217 -f 257//212 251//211 240//217 -f 257//212 240//217 239//218 -f 256//213 257//212 239//218 -f 256//213 239//218 238//219 -f 234//215 218//214 219//220 -f 234//215 219//220 224//221 -f 241//216 234//215 224//221 -f 241//216 224//221 223//222 -f 240//217 241//216 223//222 -f 240//217 223//222 222//223 -f 239//218 240//217 222//223 -f 239//218 222//223 221//224 -f 238//219 239//218 221//224 -f 238//219 221//224 220//225 -f 219//220 218//214 1285//226 -f 648//227 691//228 207//229 -f 224//221 219//220 208//230 -f 224//221 208//230 204//231 -f 223//222 224//221 204//231 -f 223//222 204//231 203//232 -f 222//223 223//222 203//232 -f 222//223 203//232 202//233 -f 221//224 222//223 202//233 -f 221//224 202//233 201//234 -f 220//225 221//224 201//234 -f 220//225 201//234 212//235 -f 204//231 208//230 191//236 -f 204//231 191//236 188//237 -f 203//232 204//231 188//237 -f 203//232 188//237 187//238 -f 202//233 203//232 187//238 -f 202//233 187//238 186//239 -f 201//234 202//233 186//239 -f 201//234 186//239 194//240 -f 212//235 201//234 194//240 -f 212//235 194//240 211//241 -f 188//237 191//236 174//242 -f 188//237 174//242 176//243 -f 187//238 188//237 176//243 -f 187//238 176//243 175//244 -f 186//239 187//238 175//244 -f 186//239 175//244 180//245 -f 194//240 186//239 180//245 -f 194//240 180//245 193//246 -f 211//241 194//240 193//246 -f 211//241 193//246 210//247 -f 613//248 585//249 1258//250 -f 176//243 174//242 168//251 -f 176//243 168//251 165//252 -f 175//244 176//243 165//252 -f 175//244 165//252 170//253 -f 180//245 175//244 170//253 -f 180//245 170//253 179//254 -f 193//246 180//245 179//254 -f 193//246 179//254 192//255 -f 210//247 193//246 192//255 -f 210//247 192//255 209//256 -f 168//251 174//242 164//257 -f 165//252 168//251 161//258 -f 165//252 161//258 162//259 -f 170//253 165//252 162//259 -f 170//253 162//259 171//260 -f 179//254 170//253 171//260 -f 179//254 171//260 181//261 -f 192//255 179//254 181//261 -f 192//255 181//261 195//262 -f 209//256 192//255 195//262 -f 209//256 195//262 213//263 -f 160//178 161//258 158//179 -f 161//258 160//178 162//259 -f 162//259 160//178 166//181 -f 171//260 162//259 166//181 -f 171//260 166//181 177//184 -f 181//261 171//260 177//184 -f 181//261 177//184 189//186 -f 195//262 181//261 189//186 -f 195//262 189//186 205//188 -f 213//263 195//262 205//188 -f 213//263 205//188 225//190 -f 242//264 225//190 235//191 -f 242//264 235//191 252//265 -f 258//266 242//264 252//265 -f 258//266 252//265 268//267 -f 273//268 258//266 268//267 -f 273//268 268//267 283//269 -f 287//270 273//268 283//269 -f 287//270 283//269 298//271 -f 300//272 287//270 298//271 -f 300//272 298//271 299//273 -f 312//274 300//272 299//273 -f 312//274 299//273 310//275 -f 252//265 235//191 253//197 -f 252//265 253//197 269//276 -f 268//267 252//265 269//276 -f 268//267 269//276 284//277 -f 283//269 268//267 284//277 -f 283//269 284//277 286//278 -f 298//271 283//269 286//278 -f 298//271 286//278 285//279 -f 299//273 298//271 285//279 -f 299//273 285//279 296//280 -f 310//275 299//273 296//280 -f 310//275 296//280 309//281 -f 269//276 253//197 267//207 -f 269//276 267//207 272//282 -f 284//277 269//276 272//282 -f 284//277 272//282 271//283 -f 286//278 284//277 271//283 -f 286//278 271//283 270//284 -f 285//279 286//278 270//284 -f 285//279 270//284 281//285 -f 296//280 285//279 281//285 -f 296//280 281//285 295//286 -f 309//281 296//280 295//286 -f 309//281 295//286 308//287 -f 272//282 267//207 256//213 -f 272//282 256//213 255//288 -f 271//283 272//282 255//288 -f 271//283 255//288 254//289 -f 270//284 271//283 254//289 -f 270//284 254//289 265//290 -f 281//285 270//284 265//290 -f 281//285 265//290 280//291 -f 295//286 281//285 280//291 -f 295//286 280//291 294//292 -f 308//287 295//286 294//292 -f 308//287 294//292 307//293 -f 255//288 256//213 238//219 -f 255//288 238//219 237//294 -f 254//289 255//288 237//294 -f 254//289 237//294 248//295 -f 265//290 254//289 248//295 -f 265//290 248//295 264//296 -f 280//291 265//290 264//296 -f 280//291 264//296 279//297 -f 294//292 280//291 279//297 -f 294//292 279//297 293//298 -f 307//293 294//292 293//298 -f 307//293 293//298 306//299 -f 237//294 238//219 220//225 -f 237//294 220//225 230//300 -f 248//295 237//294 230//300 -f 248//295 230//300 247//301 -f 264//296 248//295 247//301 -f 264//296 247//301 263//302 -f 279//297 264//296 263//302 -f 279//297 263//302 278//303 -f 293//298 279//297 278//303 -f 293//298 278//303 292//304 -f 306//299 293//298 292//304 -f 306//299 292//304 305//305 -f 230//300 220//225 212//235 -f 230//300 212//235 229//306 -f 247//301 230//300 229//306 -f 247//301 229//306 246//307 -f 263//302 247//301 246//307 -f 263//302 246//307 262//308 -f 278//303 263//302 262//308 -f 278//303 262//308 277//309 -f 292//304 278//303 277//309 -f 292//304 277//309 291//310 -f 305//305 292//304 291//310 -f 305//305 291//310 304//311 -f 229//306 212//235 211//241 -f 229//306 211//241 228//312 -f 246//307 229//306 228//312 -f 246//307 228//312 245//313 -f 262//308 246//307 245//313 -f 262//308 245//313 261//314 -f 277//309 262//308 261//314 -f 277//309 261//314 276//315 -f 291//310 277//309 276//315 -f 291//310 276//315 290//316 -f 304//311 291//310 290//316 -f 304//311 290//316 303//317 -f 228//312 211//241 210//247 -f 228//312 210//247 227//318 -f 245//313 228//312 227//318 -f 245//313 227//318 244//319 -f 261//314 245//313 244//319 -f 261//314 244//319 260//320 -f 276//315 261//314 260//320 -f 276//315 260//320 275//321 -f 290//316 276//315 275//321 -f 290//316 275//321 289//322 -f 303//317 290//316 289//322 -f 303//317 289//322 302//323 -f 227//318 210//247 209//256 -f 227//318 209//256 226//324 -f 244//319 227//318 226//324 -f 244//319 226//324 243//325 -f 260//320 244//319 243//325 -f 260//320 243//325 259//326 -f 275//321 260//320 259//326 -f 275//321 259//326 274//327 -f 289//322 275//321 274//327 -f 289//322 274//327 288//328 -f 302//323 289//322 288//328 -f 302//323 288//328 301//329 -f 226//324 209//256 213//263 -f 226//324 213//263 231//330 -f 243//325 226//324 231//330 -f 243//325 231//330 249//331 -f 259//326 243//325 249//331 -f 259//326 249//331 266//332 -f 274//327 259//326 266//332 -f 274//327 266//332 282//333 -f 288//328 274//327 282//333 -f 288//328 282//333 297//334 -f 301//329 288//328 297//334 -f 301//329 297//334 311//335 -f 231//330 213//263 225//190 -f 231//330 225//190 242//264 -f 249//331 231//330 242//264 -f 249//331 242//264 258//266 -f 266//332 249//331 258//266 -f 266//332 258//266 273//268 -f 282//333 266//332 273//268 -f 282//333 273//268 287//270 -f 297//334 282//333 287//270 -f 297//334 287//270 300//272 -f 311//335 297//334 300//272 -f 311//335 300//272 312//274 -f 312//274 310//275 323//336 -f 302//323 315//337 316//338 -f 304//311 317//339 318//340 -f 303//317 316//338 317//339 -f 309//281 308//287 321//341 -f 307//293 306//299 319//342 -f 311//335 313//343 314//344 -f 301//329 314//344 315//337 -f 312//274 324//345 313//343 -f 306//299 305//305 318//340 -f 308//287 307//293 320//346 -f 310//275 309//281 322//347 -f 322//347 321//341 325//348 -f 314//344 313//343 325//348 -f 323//336 322//347 325//348 -f 319//342 318//340 325//348 -f 315//337 314//344 325//348 -f 321//341 320//346 325//348 -f 324//345 323//336 325//348 -f 320//346 319//342 325//348 -f 317//339 316//338 325//348 -f 316//338 315//337 325//348 -f 313//343 324//345 325//348 -f 318//340 317//339 325//348 -f 326//349 327//350 328//351 -f 326//349 328//351 329//352 -f 333//353 326//349 329//352 -f 333//353 329//352 338//354 -f 347//355 333//353 338//354 -f 347//355 338//354 354//356 -f 364//357 347//355 354//356 -f 364//357 354//356 374//358 -f 386//359 364//357 374//358 -f 386//359 374//358 398//360 -f 412//361 386//359 398//360 -f 412//361 398//360 429//362 -f 329//352 328//351 334//363 -f 329//352 334//363 339//364 -f 338//354 329//352 339//364 -f 338//354 339//364 355//365 -f 354//356 338//354 355//365 -f 354//356 355//365 375//366 -f 374//358 354//356 375//366 -f 374//358 375//366 399//367 -f 398//360 374//358 399//367 -f 398//360 399//367 430//368 -f 429//362 398//360 430//368 -f 429//362 430//368 464//369 -f 339//364 334//363 342//370 -f 339//364 342//370 356//371 -f 355//365 339//364 356//371 -f 355//365 356//371 376//372 -f 375//366 355//365 376//372 -f 375//366 376//372 400//373 -f 399//367 375//366 400//373 -f 399//367 400//373 431//374 -f 430//368 399//367 431//374 -f 430//368 431//374 465//375 -f 464//369 430//368 465//375 -f 464//369 465//375 497//376 -f 356//371 342//370 351//377 -f 356//371 351//377 370//378 -f 376//372 356//371 370//378 -f 376//372 370//378 393//379 -f 400//373 376//372 393//379 -f 400//373 393//379 422//380 -f 431//374 400//373 422//380 -f 431//374 422//380 457//381 -f 465//375 431//374 457//381 -f 465//375 457//381 490//382 -f 497//376 465//375 490//382 -f 497//376 490//382 521//383 -f 370//378 351//377 361//384 -f 370//378 361//384 382//385 -f 393//379 370//378 382//385 -f 393//379 382//385 408//386 -f 422//380 393//379 408//386 -f 422//380 408//386 439//387 -f 457//381 422//380 439//387 -f 457//381 439//387 473//388 -f 490//382 457//381 473//388 -f 490//382 473//388 504//389 -f 521//383 490//382 504//389 -f 521//383 504//389 535//390 -f 382//385 361//384 369//391 -f 382//385 369//391 392//392 -f 408//386 382//385 392//392 -f 408//386 392//392 421//393 -f 439//387 408//386 421//393 -f 439//387 421//393 456//394 -f 473//388 439//387 456//394 -f 473//388 456//394 489//395 -f 504//389 473//388 489//395 -f 504//389 489//395 520//396 -f 535//390 504//389 520//396 -f 535//390 520//396 550//397 -f 392//392 369//391 380//398 -f 392//392 380//398 407//399 -f 421//393 392//392 407//399 -f 421//393 407//399 438//400 -f 456//394 421//393 438//400 -f 456//394 438//400 472//401 -f 489//395 456//394 472//401 -f 489//395 472//401 503//402 -f 520//396 489//395 503//402 -f 520//396 503//402 534//403 -f 550//397 520//396 534//403 -f 550//397 534//403 563//404 -f 407//399 380//398 391//405 -f 407//399 391//405 420//406 -f 438//400 407//399 420//406 -f 438//400 420//406 455//407 -f 472//401 438//400 455//407 -f 472//401 455//407 488//408 -f 503//402 472//401 488//408 -f 503//402 488//408 519//409 -f 534//403 503//402 519//409 -f 534//403 519//409 549//410 -f 563//404 534//403 549//410 -f 563//404 549//410 578//411 -f 420//406 391//405 404//412 -f 420//406 404//412 437//413 -f 455//407 420//406 437//413 -f 455//407 437//413 471//414 -f 488//408 455//407 471//414 -f 488//408 471//414 502//415 -f 519//409 488//408 502//415 -f 519//409 502//415 533//416 -f 549//410 519//409 533//416 -f 549//410 533//416 562//417 -f 578//411 549//410 562//417 -f 578//411 562//417 589//418 -f 437//413 404//412 419//419 -f 437//413 419//419 454//420 -f 471//414 437//413 454//420 -f 471//414 454//420 487//421 -f 502//415 471//414 487//421 -f 502//415 487//421 518//422 -f 533//416 502//415 518//422 -f 533//416 518//422 548//423 -f 562//417 533//416 548//423 -f 562//417 548//423 577//424 -f 589//418 562//417 577//424 -f 589//418 577//424 594//425 -f 454//420 419//419 436//426 -f 454//420 436//426 470//427 -f 487//421 454//420 470//427 -f 487//421 470//427 501//428 -f 518//422 487//421 501//428 -f 518//422 501//428 532//429 -f 548//423 518//422 532//429 -f 548//423 532//429 555//430 -f 577//424 548//423 555//430 -f 577//424 555//430 567//431 -f 594//425 577//424 567//431 -f 594//425 567//431 582//432 -f 470//427 436//426 453//433 -f 470//427 453//433 486//434 -f 501//428 470//427 486//434 -f 501//428 486//434 508//435 -f 532//429 501//428 508//435 -f 532//429 508//435 525//436 -f 555//430 532//429 525//436 -f 555//430 525//436 538//437 -f 567//431 555//430 538//437 -f 567//431 538//437 554//438 -f 582//432 567//431 554//438 -f 582//432 554//438 566//33 -f 486//434 453//433 462//439 -f 486//434 462//439 477//440 -f 508//435 486//434 477//440 -f 508//435 477//440 494//441 -f 525//436 508//435 494//441 -f 525//436 494//441 507//442 -f 538//437 525//436 507//442 -f 538//437 507//442 524//443 -f 554//438 538//437 524//443 -f 554//438 524//443 537//444 -f 566//33 554//438 537//444 -f 566//33 537//444 553//31 -f 477//440 462//439 443//445 -f 477//440 443//445 461//446 -f 494//441 477//440 461//446 -f 494//441 461//446 476//447 -f 507//442 494//441 476//447 -f 507//442 476//447 493//448 -f 524//443 507//442 493//448 -f 524//443 493//448 506//449 -f 537//444 524//443 506//449 -f 537//444 506//449 523//450 -f 553//31 537//444 523//450 -f 553//31 523//450 536//451 -f 461//446 443//445 426//452 -f 461//446 426//452 442//453 -f 476//447 461//446 442//453 -f 476//447 442//453 460//454 -f 493//448 476//447 460//454 -f 493//448 460//454 475//455 -f 506//449 493//448 475//455 -f 506//449 475//455 492//456 -f 523//450 506//449 492//456 -f 523//450 492//456 505//457 -f 536//451 523//450 505//457 -f 536//451 505//457 522//458 -f 442//453 426//452 411//459 -f 442//453 411//459 425//460 -f 460//454 442//453 425//460 -f 460//454 425//460 441//461 -f 475//455 460//454 441//461 -f 475//455 441//461 459//462 -f 492//456 475//455 459//462 -f 492//456 459//462 474//463 -f 505//457 492//456 474//463 -f 505//457 474//463 491//464 -f 522//458 505//457 491//464 -f 522//458 491//464 499//465 -f 425//460 411//459 396//466 -f 425//460 396//466 410//467 -f 441//461 425//460 410//467 -f 441//461 410//467 424//468 -f 459//462 441//461 424//468 -f 459//462 424//468 440//469 -f 474//463 459//462 440//469 -f 474//463 440//469 458//470 -f 491//464 474//463 458//470 -f 491//464 458//470 467//471 -f 499//465 491//464 467//471 -f 499//465 467//471 466//472 -f 410//467 396//466 385//473 -f 410//467 385//473 395//474 -f 424//468 410//467 395//474 -f 424//468 395//474 409//475 -f 440//469 424//468 409//475 -f 440//469 409//475 423//476 -f 458//470 440//469 423//476 -f 458//470 423//476 433//477 -f 467//471 458//470 433//477 -f 467//471 433//477 432//478 -f 466//472 467//471 432//478 -f 466//472 432//478 434//479 -f 395//474 385//473 372//480 -f 395//474 372//480 383//481 -f 409//475 395//474 383//481 -f 409//475 383//481 394//482 -f 423//476 409//475 394//482 -f 423//476 394//482 402//483 -f 433//477 423//476 402//483 -f 433//477 402//483 401//484 -f 432//478 433//477 401//484 -f 432//478 401//484 403//485 -f 434//479 432//478 403//485 -f 434//479 403//485 417//486 -f 383//481 372//480 362//487 -f 383//481 362//487 371//488 -f 394//482 383//481 371//488 -f 394//482 371//488 378//489 -f 402//483 394//482 378//489 -f 402//483 378//489 377//490 -f 401//484 402//483 377//490 -f 401//484 377//490 379//491 -f 403//485 401//484 379//491 -f 403//485 379//491 390//492 -f 417//486 403//485 390//492 -f 417//486 390//492 416//493 -f 371//488 362//487 352//494 -f 371//488 352//494 358//495 -f 378//489 371//488 358//495 -f 378//489 358//495 357//496 -f 377//490 378//489 357//496 -f 377//490 357//496 359//497 -f 379//491 377//490 359//497 -f 379//491 359//497 367//498 -f 390//492 379//491 367//498 -f 390//492 367//498 389//499 -f 416//493 390//492 389//499 -f 416//493 389//499 415//500 -f 358//495 352//494 345//501 -f 358//495 345//501 340//502 -f 357//496 358//495 340//502 -f 357//496 340//502 341//503 -f 359//497 357//496 341//503 -f 359//497 341//503 349//504 -f 367//498 359//497 349//504 -f 367//498 349//504 366//505 -f 389//499 367//498 366//505 -f 389//499 366//505 388//506 -f 415//500 389//499 388//506 -f 415//500 388//506 414//507 -f 340//502 345//501 335//508 -f 340//502 335//508 330//509 -f 341//503 340//502 330//509 -f 341//503 330//509 336//510 -f 349//504 341//503 336//510 -f 349//504 336//510 348//511 -f 366//505 349//504 348//511 -f 366//505 348//511 365//512 -f 388//506 366//505 365//512 -f 388//506 365//512 387//513 -f 414//507 388//506 387//513 -f 414//507 387//513 413//514 -f 330//509 335//508 327//350 -f 327//350 326//349 330//509 -f 330//509 326//349 336//510 -f 336//510 326//349 333//353 -f 348//511 336//510 333//353 -f 348//511 333//353 347//355 -f 365//512 348//511 347//355 -f 365//512 347//355 364//357 -f 387//513 365//512 364//357 -f 387//513 364//357 386//359 -f 413//514 387//513 386//359 -f 413//514 386//359 412//361 -f 445//515 412//361 429//362 -f 445//515 429//362 463//516 -f 478//517 445//515 463//516 -f 478//517 463//516 495//518 -f 509//176 478//517 495//518 -f 509//176 495//518 526//519 -f 463//516 429//362 464//369 -f 463//516 464//369 496//520 -f 495//518 463//516 496//520 -f 495//518 496//520 527//521 -f 526//519 495//518 527//521 -f 526//519 527//521 557//522 -f 556//523 526//519 557//522 -f 556//523 557//522 583//524 -f 595//202 556//523 583//524 -f 595//202 583//524 611//525 -f 621//526 595//202 611//525 -f 621//526 611//525 638//527 -f 496//520 464//369 497//376 -f 496//520 497//376 528//528 -f 527//521 496//520 528//528 -f 527//521 528//528 558//529 -f 557//522 527//521 558//529 -f 557//522 558//529 584//530 -f 583//524 557//522 584//530 -f 583//524 584//530 612//531 -f 611//525 583//524 612//531 -f 611//525 612//531 639//532 -f 638//527 611//525 639//532 -f 638//527 639//532 664//533 -f 528//528 497//376 521//383 -f 528//528 521//383 551//534 -f 558//529 528//528 551//534 -f 558//529 551//534 580//535 -f 584//530 558//529 580//535 -f 584//530 580//535 608//536 -f 612//531 584//530 608//536 -f 612//531 608//536 634//537 -f 639//532 612//531 634//537 -f 639//532 634//537 661//538 -f 664//533 639//532 661//538 -f 664//533 661//538 688//539 -f 551//534 521//383 535//390 -f 551//534 535//390 564//540 -f 580//535 551//534 564//540 -f 580//535 564//540 591//541 -f 608//536 580//535 591//541 -f 608//536 591//541 618//542 -f 634//537 608//536 618//542 -f 634//537 618//542 644//543 -f 661//538 634//537 644//543 -f 661//538 644//543 670//544 -f 688//539 661//538 670//544 -f 688//539 670//544 698//545 -f 564//540 535//390 550//397 -f 564//540 550//397 579//546 -f 591//541 564//540 579//546 -f 591//541 579//546 607//547 -f 618//542 591//541 607//547 -f 618//542 607//547 633//548 -f 644//543 618//542 633//548 -f 644//543 633//548 660//549 -f 670//544 644//543 660//549 -f 670//544 660//549 687//550 -f 698//545 670//544 687//550 -f 698//545 687//550 717//551 -f 579//546 550//397 563//404 -f 579//546 563//404 590//552 -f 607//547 579//546 590//552 -f 607//547 590//552 617//553 -f 633//548 607//547 617//553 -f 633//548 617//553 643//554 -f 660//549 633//548 643//554 -f 660//549 643//554 669//555 -f 687//550 660//549 669//555 -f 687//550 669//555 697//556 -f 717//551 687//550 697//556 -f 717//551 697//556 727//557 -f 590//552 563//404 578//411 -f 590//552 578//411 606//558 -f 617//553 590//552 606//558 -f 617//553 606//558 632//559 -f 643//554 617//553 632//559 -f 643//554 632//559 659//560 -f 669//555 643//554 659//560 -f 669//555 659//560 673//561 -f 697//556 669//555 673//561 -f 697//556 673//561 700//562 -f 727//557 697//556 700//562 -f 727//557 700//562 699//563 -f 606//558 578//411 589//418 -f 606//558 589//418 616//564 -f 632//559 606//558 616//564 -f 632//559 616//564 637//565 -f 659//560 632//559 637//565 -f 659//560 637//565 647//566 -f 673//561 659//560 647//566 -f 673//561 647//566 672//567 -f 700//562 673//561 672//567 -f 700//562 672//567 671//568 -f 699//563 700//562 671//568 -f 699//563 671//568 689//569 -f 616//564 589//418 594//425 -f 616//564 594//425 610//570 -f 637//565 616//564 610//570 -f 637//565 610//570 620//571 -f 647//566 637//565 620//571 -f 647//566 620//571 646//572 -f 672//567 647//566 646//572 -f 672//567 646//572 645//573 -f 671//568 672//567 645//573 -f 671//568 645//573 662//574 -f 689//569 671//568 662//574 -f 689//569 662//574 668//575 -f 610//570 594//425 582//432 -f 610//570 582//432 593//3 -f 620//571 610//570 593//3 -f 620//571 593//3 636//89 -f 646//572 620//571 636//89 -f 646//572 636//89 619//576 -f 645//573 646//572 619//576 -f 645//573 619//576 635//577 -f 662//574 645//573 635//577 -f 662//574 635//577 642//578 -f 668//575 662//574 642//578 -f 668//575 642//578 641//579 -f 593//3 582//432 566//33 -f 619//576 636//89 592//57 -f 619//576 592//57 609//580 -f 635//577 619//576 609//580 -f 635//577 609//580 615//581 -f 642//578 635//577 615//581 -f 642//578 615//581 614//582 -f 641//579 642//578 614//582 -f 592//57 565//56 581//583 -f 609//580 592//57 581//583 -f 609//580 581//583 587//584 -f 615//581 609//580 587//584 -f 615//581 587//584 586//585 -f 614//582 615//581 586//585 -f 614//582 586//585 588//586 -f 565//56 553//31 536//451 -f 565//56 536//451 552//587 -f 581//583 565//56 552//587 -f 581//583 552//587 560//588 -f 587//584 581//583 560//588 -f 587//584 560//588 559//589 -f 586//585 587//584 559//589 -f 586//585 559//589 561//590 -f 588//586 586//585 561//590 -f 588//586 561//590 576//591 -f 605//592 588//586 576//591 -f 605//592 576//591 604//593 -f 552//587 536//451 522//458 -f 552//587 522//458 530//594 -f 560//588 552//587 530//594 -f 560//588 530//594 529//595 -f 559//589 560//588 529//595 -f 559//589 529//595 531//596 -f 561//590 559//589 531//596 -f 561//590 531//596 547//597 -f 576//591 561//590 547//597 -f 576//591 547//597 575//598 -f 604//593 576//591 575//598 -f 604//593 575//598 603//599 -f 530//594 522//458 499//465 -f 530//594 499//465 498//600 -f 529//595 530//594 498//600 -f 529//595 498//600 500//601 -f 531//596 529//595 500//601 -f 531//596 500//601 517//602 -f 547//597 531//596 517//602 -f 547//597 517//602 546//603 -f 575//598 547//597 546//603 -f 575//598 546//603 574//604 -f 603//599 575//598 574//604 -f 603//599 574//604 602//605 -f 498//600 499//465 466//472 -f 498//600 466//472 468//606 -f 500//601 498//600 468//606 -f 500//601 468//606 485//607 -f 517//602 500//601 485//607 -f 517//602 485//607 516//608 -f 546//603 517//602 516//608 -f 546//603 516//608 545//609 -f 574//604 546//603 545//609 -f 574//604 545//609 573//610 -f 602//605 574//604 573//610 -f 602//605 573//610 601//611 -f 468//606 466//472 434//479 -f 468//606 434//479 451//612 -f 485//607 468//606 451//612 -f 485//607 451//612 484//613 -f 516//608 485//607 484//613 -f 516//608 484//613 515//614 -f 545//609 516//608 515//614 -f 545//609 515//614 544//615 -f 573//610 545//609 544//615 -f 573//610 544//615 572//616 -f 601//611 573//610 572//616 -f 601//611 572//616 600//617 -f 451//612 434//479 417//486 -f 451//612 417//486 450//618 -f 484//613 451//612 450//618 -f 484//613 450//618 483//619 -f 515//614 484//613 483//619 -f 515//614 483//619 514//620 -f 544//615 515//614 514//620 -f 544//615 514//620 543//621 -f 572//616 544//615 543//621 -f 572//616 543//621 571//622 -f 600//617 572//616 571//622 -f 600//617 571//622 599//623 -f 450//618 417//486 416//493 -f 450//618 416//493 449//624 -f 483//619 450//618 449//624 -f 483//619 449//624 482//625 -f 514//620 483//619 482//625 -f 514//620 482//625 513//626 -f 543//621 514//620 513//626 -f 543//621 513//626 542//627 -f 571//622 543//621 542//627 -f 571//622 542//627 570//628 -f 599//623 571//622 570//628 -f 599//623 570//628 598//629 -f 449//624 416//493 415//500 -f 449//624 415//500 448//630 -f 482//625 449//624 448//630 -f 482//625 448//630 481//631 -f 513//626 482//625 481//631 -f 513//626 481//631 512//632 -f 542//627 513//626 512//632 -f 542//627 512//632 541//633 -f 570//628 542//627 541//633 -f 570//628 541//633 569//634 -f 598//629 570//628 569//634 -f 598//629 569//634 597//635 -f 448//630 415//500 414//507 -f 448//630 414//507 447//636 -f 481//631 448//630 447//636 -f 481//631 447//636 480//637 -f 512//632 481//631 480//637 -f 512//632 480//637 511//638 -f 541//633 512//632 511//638 -f 541//633 511//638 540//639 -f 569//634 541//633 540//639 -f 569//634 540//639 568//640 -f 597//635 569//634 568//640 -f 597//635 568//640 596//641 -f 447//636 414//507 413//514 -f 447//636 413//514 446//642 -f 480//637 447//636 446//642 -f 480//637 446//642 479//643 -f 511//638 480//637 479//643 -f 511//638 479//643 510//644 -f 540//639 511//638 510//644 -f 540//639 510//644 539//175 -f 568//640 540//639 539//175 -f 568//640 539//175 585//249 -f 596//641 568//640 585//249 -f 596//641 585//249 613//248 -f 446//642 413//514 412//361 -f 446//642 412//361 445//515 -f 479//643 446//642 445//515 -f 479//643 445//515 478//517 -f 510//644 479//643 478//517 -f 510//644 478//517 509//176 -f 539//175 510//644 509//176 -f 691//228 648//227 674//645 -f 702//646 691//228 674//645 -f 702//646 674//645 701//647 -f 731//648 702//646 701//647 -f 731//648 701//647 730//649 -f 759//650 731//648 730//649 -f 759//650 730//649 758//651 -f 786//652 759//650 758//651 -f 786//652 758//651 785//653 -f 648//227 621//526 638//527 -f 648//227 638//527 663//654 -f 674//645 648//227 663//654 -f 674//645 663//654 690//655 -f 701//647 674//645 690//655 -f 701//647 690//655 720//656 -f 730//649 701//647 720//656 -f 730//649 720//656 749//657 -f 758//651 730//649 749//657 -f 758//651 749//657 777//658 -f 785//653 758//651 777//658 -f 785//653 777//658 805//659 -f 663//654 638//527 664//533 -f 663//654 664//533 692//660 -f 690//655 663//654 692//660 -f 690//655 692//660 721//661 -f 720//656 690//655 721//661 -f 720//656 721//661 750//662 -f 749//657 720//656 750//662 -f 749//657 750//662 778//663 -f 777//658 749//657 778//663 -f 777//658 778//663 806//664 -f 805//659 777//658 806//664 -f 805//659 806//664 833//665 -f 692//660 664//533 688//539 -f 692//660 688//539 718//666 -f 721//661 692//660 718//666 -f 721//661 718//666 747//667 -f 750//662 721//661 747//667 -f 750//662 747//667 775//668 -f 778//663 750//662 775//668 -f 778//663 775//668 803//669 -f 806//664 778//663 803//669 -f 806//664 803//669 831//670 -f 833//665 806//664 831//670 -f 833//665 831//670 838//671 -f 718//666 688//539 698//545 -f 718//666 698//545 728//672 -f 747//667 718//666 728//672 -f 747//667 728//672 756//673 -f 775//668 747//667 756//673 -f 775//668 756//673 784//674 -f 803//669 775//668 784//674 -f 803//669 784//674 804//675 -f 831//670 803//669 804//675 -f 831//670 804//675 811//676 -f 838//671 831//670 811//676 -f 838//671 811//676 830//677 -f 728//672 698//545 717//551 -f 728//672 717//551 746//678 -f 756//673 728//672 746//678 -f 756//673 746//678 757//679 -f 784//674 756//673 757//679 -f 784//674 757//679 776//680 -f 804//675 784//674 776//680 -f 804//675 776//680 783//681 -f 811//676 804//675 783//681 -f 811//676 783//681 802//682 -f 830//677 811//676 802//682 -f 830//677 802//682 810//683 -f 746//678 717//551 727//557 -f 746//678 727//557 729//684 -f 757//679 746//678 729//684 -f 757//679 729//684 748//685 -f 776//680 757//679 748//685 -f 776//680 748//685 755//686 -f 783//681 776//680 755//686 -f 783//681 755//686 774//687 -f 802//682 783//681 774//687 -f 802//682 774//687 782//688 -f 810//683 802//682 782//688 -f 810//683 782//688 809//689 -f 729//684 727//557 699//563 -f 729//684 699//563 719//690 -f 748//685 729//684 719//690 -f 748//685 719//690 726//691 -f 755//686 748//685 726//691 -f 755//686 726//691 745//692 -f 774//687 755//686 745//692 -f 774//687 745//692 754//693 -f 782//688 774//687 754//693 -f 782//688 754//693 781//694 -f 809//689 782//688 781//694 -f 809//689 781//694 780//695 -f 719//690 699//563 689//569 -f 719//690 689//569 696//696 -f 726//691 719//690 696//696 -f 726//691 696//696 716//697 -f 745//692 726//691 716//697 -f 745//692 716//697 725//698 -f 754//693 745//692 725//698 -f 754//693 725//698 753//699 -f 781//694 754//693 753//699 -f 781//694 753//699 752//700 -f 780//695 781//694 752//700 -f 780//695 752//700 773//701 -f 696//696 689//569 668//575 -f 696//696 668//575 686//702 -f 716//697 696//696 686//702 -f 716//697 686//702 695//703 -f 725//698 716//697 695//703 -f 725//698 695//703 724//704 -f 753//699 725//698 724//704 -f 753//699 724//704 723//705 -f 752//700 753//699 723//705 -f 752//700 723//705 744//706 -f 773//701 752//700 744//706 -f 773//701 744//706 772//707 -f 686//702 668//575 641//579 -f 686//702 641//579 667//708 -f 695//703 686//702 667//708 -f 695//703 667//708 715//160 -f 724//704 695//703 715//160 -f 724//704 715//160 694//709 -f 723//705 724//704 694//709 -f 723//705 694//709 714//710 -f 744//706 723//705 714//710 -f 744//706 714//710 743//711 -f 772//707 744//706 743//711 -f 772//707 743//711 771//712 -f 694//709 715//160 666//713 -f 694//709 666//713 685//714 -f 714//710 694//709 685//714 -f 714//710 685//714 713//715 -f 743//711 714//710 713//715 -f 743//711 713//715 742//716 -f 771//712 743//711 742//716 -f 771//712 742//716 770//717 -f 666//713 631//152 658//718 -f 685//714 666//713 658//718 -f 685//714 658//718 684//719 -f 713//715 685//714 684//719 -f 713//715 684//719 712//720 -f 742//716 713//715 712//720 -f 742//716 712//720 741//721 -f 770//717 742//716 741//721 -f 770//717 741//721 769//722 -f 631//152 605//592 604//593 -f 631//152 604//593 630//723 -f 658//718 631//152 630//723 -f 658//718 630//723 657//724 -f 684//719 658//718 657//724 -f 684//719 657//724 683//725 -f 712//720 684//719 683//725 -f 712//720 683//725 711//726 -f 741//721 712//720 711//726 -f 741//721 711//726 740//727 -f 769//722 741//721 740//727 -f 769//722 740//727 768//728 -f 630//723 604//593 603//599 -f 630//723 603//599 629//729 -f 657//724 630//723 629//729 -f 657//724 629//729 656//730 -f 683//725 657//724 656//730 -f 683//725 656//730 682//731 -f 711//726 683//725 682//731 -f 711//726 682//731 710//732 -f 740//727 711//726 710//732 -f 740//727 710//732 739//733 -f 768//728 740//727 739//733 -f 768//728 739//733 767//734 -f 629//729 603//599 602//605 -f 629//729 602//605 628//735 -f 656//730 629//729 628//735 -f 656//730 628//735 655//736 -f 682//731 656//730 655//736 -f 682//731 655//736 681//737 -f 710//732 682//731 681//737 -f 710//732 681//737 709//738 -f 739//733 710//732 709//738 -f 739//733 709//738 738//739 -f 767//734 739//733 738//739 -f 767//734 738//739 766//740 -f 628//735 602//605 601//611 -f 628//735 601//611 627//741 -f 655//736 628//735 627//741 -f 655//736 627//741 654//742 -f 681//737 655//736 654//742 -f 681//737 654//742 680//743 -f 709//738 681//737 680//743 -f 709//738 680//743 708//744 -f 738//739 709//738 708//744 -f 738//739 708//744 737//745 -f 766//740 738//739 737//745 -f 766//740 737//745 765//746 -f 627//741 601//611 600//617 -f 627//741 600//617 626//747 -f 654//742 627//741 626//747 -f 654//742 626//747 653//748 -f 680//743 654//742 653//748 -f 680//743 653//748 679//749 -f 708//744 680//743 679//749 -f 708//744 679//749 707//750 -f 737//745 708//744 707//750 -f 737//745 707//750 736//751 -f 765//746 737//745 736//751 -f 765//746 736//751 764//752 -f 626//747 600//617 599//623 -f 626//747 599//623 625//753 -f 653//748 626//747 625//753 -f 653//748 625//753 652//754 -f 679//749 653//748 652//754 -f 679//749 652//754 678//755 -f 707//750 679//749 678//755 -f 707//750 678//755 706//756 -f 736//751 707//750 706//756 -f 736//751 706//756 735//757 -f 764//752 736//751 735//757 -f 764//752 735//757 763//758 -f 625//753 599//623 598//629 -f 625//753 598//629 624//759 -f 652//754 625//753 624//759 -f 652//754 624//759 651//760 -f 678//755 652//754 651//760 -f 678//755 651//760 677//761 -f 706//756 678//755 677//761 -f 706//756 677//761 705//762 -f 735//757 706//756 705//762 -f 735//757 705//762 734//763 -f 763//758 735//757 734//763 -f 763//758 734//763 762//764 -f 624//759 598//629 597//635 -f 624//759 597//635 623//765 -f 651//760 624//759 623//765 -f 651//760 623//765 650//766 -f 677//761 651//760 650//766 -f 677//761 650//766 676//767 -f 705//762 677//761 676//767 -f 705//762 676//767 704//768 -f 734//763 705//762 704//768 -f 734//763 704//768 733//769 -f 762//764 734//763 733//769 -f 762//764 733//769 761//770 -f 623//765 597//635 596//641 -f 623//765 596//641 622//771 -f 650//766 623//765 622//771 -f 650//766 622//771 649//772 -f 676//767 650//766 649//772 -f 676//767 649//772 675//773 -f 704//768 676//767 675//773 -f 704//768 675//773 703//774 -f 733//769 704//768 703//774 -f 733//769 703//774 732//775 -f 761//770 733//769 732//775 -f 761//770 732//775 760//776 -f 622//771 596//641 613//248 -f 622//771 613//248 640//777 -f 649//772 622//771 640//777 -f 649//772 640//777 665//778 -f 675//773 649//772 665//778 -f 675//773 665//778 693//779 -f 703//774 675//773 693//779 -f 703//774 693//779 722//780 -f 732//775 703//774 722//780 -f 732//775 722//780 751//781 -f 760//776 732//775 751//781 -f 760//776 751//781 779//782 -f 693//779 665//778 691//228 -f 693//779 691//228 702//646 -f 722//780 693//779 702//646 -f 722//780 702//646 731//648 -f 751//781 722//780 731//648 -f 751//781 731//648 759//650 -f 779//782 751//781 759//650 -f 779//782 759//650 786//652 -f 917//783 918//784 913//785 -f 892//786 917//783 913//785 -f 892//786 913//785 887//787 -f 866//788 892//786 887//787 -f 866//788 887//787 861//789 -f 840//790 866//788 861//789 -f 840//790 861//789 834//791 -f 813//792 840//790 834//791 -f 813//792 834//791 807//793 -f 786//652 813//792 807//793 -f 786//652 807//793 779//782 -f 913//785 918//784 893//794 -f 887//787 913//785 893//794 -f 887//787 893//794 867//795 -f 861//789 887//787 867//795 -f 861//789 867//795 841//796 -f 834//791 861//789 841//796 -f 834//791 841//796 814//797 -f 807//793 834//791 814//797 -f 807//793 814//797 787//798 -f 779//782 807//793 787//798 -f 779//782 787//798 760//776 -f 893//794 918//784 894//799 -f 867//795 893//794 894//799 -f 867//795 894//799 868//800 -f 841//796 867//795 868//800 -f 841//796 868//800 842//801 -f 814//797 841//796 842//801 -f 814//797 842//801 815//802 -f 787//798 814//797 815//802 -f 787//798 815//802 788//803 -f 760//776 787//798 788//803 -f 760//776 788//803 761//770 -f 894//799 918//784 895//804 -f 868//800 894//799 895//804 -f 868//800 895//804 869//805 -f 842//801 868//800 869//805 -f 842//801 869//805 843//806 -f 815//802 842//801 843//806 -f 815//802 843//806 816//807 -f 788//803 815//802 816//807 -f 788//803 816//807 789//808 -f 761//770 788//803 789//808 -f 761//770 789//808 762//764 -f 895//804 918//784 896//809 -f 869//805 895//804 896//809 -f 869//805 896//809 870//810 -f 843//806 869//805 870//810 -f 843//806 870//810 844//811 -f 816//807 843//806 844//811 -f 816//807 844//811 817//812 -f 789//808 816//807 817//812 -f 789//808 817//812 790//813 -f 762//764 789//808 790//813 -f 762//764 790//813 763//758 -f 896//809 918//784 897//814 -f 870//810 896//809 897//814 -f 870//810 897//814 871//815 -f 844//811 870//810 871//815 -f 844//811 871//815 845//816 -f 817//812 844//811 845//816 -f 817//812 845//816 818//817 -f 790//813 817//812 818//817 -f 790//813 818//817 791//818 -f 763//758 790//813 791//818 -f 763//758 791//818 764//752 -f 897//814 918//784 898//819 -f 871//815 897//814 898//819 -f 871//815 898//819 872//820 -f 845//816 871//815 872//820 -f 845//816 872//820 846//821 -f 818//817 845//816 846//821 -f 818//817 846//821 819//822 -f 791//818 818//817 819//822 -f 791//818 819//822 792//823 -f 764//752 791//818 792//823 -f 764//752 792//823 765//746 -f 898//819 918//784 899//824 -f 872//820 898//819 899//824 -f 872//820 899//824 873//825 -f 846//821 872//820 873//825 -f 846//821 873//825 847//826 -f 819//822 846//821 847//826 -f 819//822 847//826 820//827 -f 792//823 819//822 820//827 -f 792//823 820//827 793//828 -f 765//746 792//823 793//828 -f 765//746 793//828 766//740 -f 899//824 918//784 900//829 -f 873//825 899//824 900//829 -f 873//825 900//829 874//830 -f 847//826 873//825 874//830 -f 847//826 874//830 848//831 -f 820//827 847//826 848//831 -f 820//827 848//831 821//832 -f 793//828 820//827 821//832 -f 793//828 821//832 794//833 -f 766//740 793//828 794//833 -f 766//740 794//833 767//734 -f 900//829 918//784 901//834 -f 874//830 900//829 901//834 -f 874//830 901//834 875//835 -f 848//831 874//830 875//835 -f 848//831 875//835 849//836 -f 821//832 848//831 849//836 -f 821//832 849//836 822//837 -f 794//833 821//832 822//837 -f 794//833 822//837 795//838 -f 767//734 794//833 795//838 -f 767//734 795//838 768//728 -f 901//834 918//784 902//839 -f 875//835 901//834 902//839 -f 875//835 902//839 876//840 -f 849//836 875//835 876//840 -f 849//836 876//840 850//841 -f 822//837 849//836 850//841 -f 822//837 850//841 823//842 -f 795//838 822//837 823//842 -f 795//838 823//842 796//843 -f 768//728 795//838 796//843 -f 768//728 796//843 769//722 -f 902//839 918//784 903//844 -f 876//840 902//839 903//844 -f 876//840 903//844 877//845 -f 850//841 876//840 877//845 -f 850//841 877//845 851//846 -f 823//842 850//841 851//846 -f 823//842 851//846 824//847 -f 796//843 823//842 824//847 -f 796//843 824//847 797//848 -f 769//722 796//843 797//848 -f 769//722 797//848 770//717 -f 903//844 918//784 904//849 -f 877//845 903//844 904//849 -f 877//845 904//849 878//850 -f 851//846 877//845 878//850 -f 851//846 878//850 852//851 -f 824//847 851//846 852//851 -f 824//847 852//851 825//852 -f 797//848 824//847 825//852 -f 797//848 825//852 798//853 -f 770//717 797//848 798//853 -f 770//717 798//853 771//712 -f 904//849 918//784 905//854 -f 878//850 904//849 905//854 -f 878//850 905//854 879//855 -f 852//851 878//850 879//855 -f 852//851 879//855 853//856 -f 825//852 852//851 853//856 -f 825//852 853//856 826//857 -f 798//853 825//852 826//857 -f 798//853 826//857 799//858 -f 771//712 798//853 799//858 -f 771//712 799//858 772//707 -f 905//854 918//784 906//859 -f 879//855 905//854 906//859 -f 879//855 906//859 880//860 -f 853//856 879//855 880//860 -f 853//856 880//860 854//861 -f 826//857 853//856 854//861 -f 826//857 854//861 827//862 -f 799//858 826//857 827//862 -f 799//858 827//862 800//863 -f 772//707 799//858 800//863 -f 772//707 800//863 773//701 -f 906//859 918//784 907//864 -f 880//860 906//859 907//864 -f 880//860 907//864 881//865 -f 854//861 880//860 881//865 -f 854//861 881//865 855//866 -f 827//862 854//861 855//866 -f 827//862 855//866 828//867 -f 800//863 827//862 828//867 -f 800//863 828//867 801//868 -f 773//701 800//863 801//868 -f 773//701 801//868 780//695 -f 907//864 918//784 908//869 -f 881//865 907//864 908//869 -f 881//865 908//869 882//870 -f 855//866 881//865 882//870 -f 855//866 882//870 856//871 -f 828//867 855//866 856//871 -f 828//867 856//871 829//872 -f 801//868 828//867 829//872 -f 801//868 829//872 808//873 -f 780//695 801//868 808//873 -f 780//695 808//873 809//689 -f 908//869 918//784 909//874 -f 882//870 908//869 909//874 -f 882//870 909//874 883//875 -f 856//871 882//870 883//875 -f 856//871 883//875 857//876 -f 829//872 856//871 857//876 -f 829//872 857//876 836//877 -f 808//873 829//872 836//877 -f 808//873 836//877 835//878 -f 809//689 808//873 835//878 -f 809//689 835//878 810//683 -f 909//874 918//784 910//879 -f 883//875 909//874 910//879 -f 883//875 910//879 884//880 -f 857//876 883//875 884//880 -f 857//876 884//880 863//881 -f 836//877 857//876 863//881 -f 836//877 863//881 862//882 -f 835//878 836//877 862//882 -f 835//878 862//882 837//883 -f 810//683 835//878 837//883 -f 810//683 837//883 830//677 -f 910//879 918//784 911//884 -f 884//880 910//879 911//884 -f 884//880 911//884 889//885 -f 863//881 884//880 889//885 -f 863//881 889//885 888//886 -f 862//882 863//881 888//886 -f 862//882 888//886 864//887 -f 837//883 862//882 864//887 -f 837//883 864//887 858//888 -f 830//677 837//883 858//888 -f 830//677 858//888 838//671 -f 911//884 918//784 915//889 -f 889//885 911//884 915//889 -f 889//885 915//889 914//890 -f 888//886 889//885 914//890 -f 888//886 914//890 890//891 -f 864//887 888//886 890//891 -f 864//887 890//891 885//892 -f 858//888 864//887 885//892 -f 858//888 885//892 859//893 -f 838//671 858//888 859//893 -f 838//671 859//893 833//665 -f 915//889 918//784 919//894 -f 914//890 915//889 919//894 -f 914//890 919//894 912//895 -f 890//891 914//890 912//895 -f 890//891 912//895 886//896 -f 885//892 890//891 886//896 -f 885//892 886//896 860//897 -f 859//893 885//892 860//897 -f 859//893 860//897 832//898 -f 833//665 859//893 832//898 -f 833//665 832//898 805//659 -f 919//894 918//784 916//899 -f 912//895 919//894 916//899 -f 912//895 916//899 891//900 -f 886//896 912//895 891//900 -f 886//896 891//900 865//901 -f 860//897 886//896 865//901 -f 860//897 865//901 839//902 -f 832//898 860//897 839//902 -f 832//898 839//902 812//903 -f 805//659 832//898 812//903 -f 805//659 812//903 785//653 -f 916//899 918//784 917//783 -f 891//900 916//899 917//783 -f 891//900 917//783 892//786 -f 865//901 891//900 892//786 -f 865//901 892//786 866//788 -f 839//902 865//901 866//788 -f 839//902 866//788 840//790 -f 812//903 839//902 840//790 -f 812//903 840//790 813//792 -f 785//653 812//903 813//792 -f 785//653 813//792 786//652 -f 404//412 1267//904 1268//905 -f 384//906 373//907 372//480 -f 346//908 345//501 352//494 -f 435//909 436//426 419//419 -f 331//910 327//350 335//508 -f 462//439 453//433 1265//911 -f 920//912 921//913 922//914 -f 920//912 922//914 923//915 -f 923//915 922//914 927//916 -f 923//915 927//916 933//917 -f 933//917 927//916 940//918 -f 933//917 940//918 949//919 -f 949//919 940//918 959//920 -f 949//919 959//920 971//921 -f 971//921 959//920 984//922 -f 971//921 984//922 999//923 -f 999//923 984//922 1011//924 -f 999//923 1011//924 1027//925 -f 1027//925 1011//924 1042//926 -f 1027//925 1042//926 1059//927 -f 1059//927 1042//926 1077//928 -f 1059//927 1077//928 1095//929 -f 1095//929 1077//928 1110//930 -f 1095//929 1110//930 1129//931 -f 1129//931 1110//930 1144//932 -f 1129//931 1144//932 1163//933 -f 1163//933 1144//932 1179//934 -f 1163//933 1179//934 1196//935 -f 1196//935 1179//934 1210//936 -f 1196//935 1210//936 1220//937 -f 1220//937 1210//936 1212//938 -f 1220//937 1212//938 1201//939 -f 1201//939 1212//938 1181//940 -f 1201//939 1181//940 1169//941 -f 1169//941 1181//940 1147//942 -f 1169//941 1147//942 1135//943 -f 1135//943 1147//942 1113//944 -f 1135//943 1113//944 1102//945 -f 1102//945 1113//944 1081//946 -f 1102//945 1081//946 1065//947 -f 1065//947 1081//946 1046//948 -f 1065//947 1046//948 1032//949 -f 1032//949 1046//948 1016//950 -f 1032//949 1016//950 1003//951 -f 1003//951 1016//950 988//952 -f 1003//951 988//952 974//953 -f 974//953 988//952 962//954 -f 974//953 962//954 951//955 -f 951//955 962//954 942//956 -f 951//955 942//956 934//957 -f 934//957 942//956 928//958 -f 934//957 928//958 924//959 -f 924//959 928//958 921//913 -f 921//913 920//912 924//959 -f 926//960 920//912 923//915 -f 926//960 923//915 932//961 -f 938//962 926//960 932//961 -f 938//962 932//961 947//963 -f 956//964 938//962 947//963 -f 956//964 947//963 968//965 -f 980//966 956//964 968//965 -f 980//966 968//965 995//967 -f 1006//968 980//966 995//967 -f 1006//968 995//967 1022//969 -f 1036//970 1006//968 1022//969 -f 1036//970 1022//969 1053//971 -f 932//961 923//915 933//917 -f 932//961 933//917 948//972 -f 947//963 932//961 948//972 -f 947//963 948//972 969//973 -f 968//965 947//963 969//973 -f 968//965 969//973 996//974 -f 995//967 968//965 996//974 -f 995//967 996//974 1023//975 -f 1022//969 995//967 1023//975 -f 1022//969 1023//975 1054//976 -f 1053//971 1022//969 1054//976 -f 1053//971 1054//976 1089//977 -f 948//972 933//917 949//919 -f 948//972 949//919 970//978 -f 969//973 948//972 970//978 -f 969//973 970//978 997//979 -f 996//974 969//973 997//979 -f 996//974 997//979 1024//980 -f 1023//975 996//974 1024//980 -f 1023//975 1024//980 1055//981 -f 1054//976 1023//975 1055//981 -f 1054//976 1055//981 1090//982 -f 1089//977 1054//976 1090//982 -f 1089//977 1090//982 1109//983 -f 970//978 949//919 971//921 -f 970//978 971//921 998//984 -f 997//979 970//978 998//984 -f 997//979 998//984 1025//985 -f 1024//980 997//979 1025//985 -f 1024//980 1025//985 1056//986 -f 1055//981 1024//980 1056//986 -f 1055//981 1056//986 1091//987 -f 1090//982 1055//981 1091//987 -f 1090//982 1091//987 1124//988 -f 1109//983 1090//982 1124//988 -f 1109//983 1124//988 1122//989 -f 998//984 971//921 999//923 -f 998//984 999//923 1026//990 -f 1025//985 998//984 1026//990 -f 1025//985 1026//990 1057//991 -f 1056//986 1025//985 1057//991 -f 1056//986 1057//991 1092//992 -f 1091//987 1056//986 1092//992 -f 1091//987 1092//992 1125//993 -f 1124//988 1091//987 1125//993 -f 1124//988 1125//993 1156//994 -f 1122//989 1124//988 1156//994 -f 1122//989 1156//994 1142//995 -f 1026//990 999//923 1027//925 -f 1026//990 1027//925 1058//996 -f 1057//991 1026//990 1058//996 -f 1057//991 1058//996 1093//997 -f 1092//992 1057//991 1093//997 -f 1092//992 1093//997 1126//998 -f 1125//993 1092//992 1126//998 -f 1125//993 1126//998 1159//999 -f 1156//994 1125//993 1159//999 -f 1156//994 1159//999 1176//1000 -f 1142//995 1156//994 1176//1000 -f 1142//995 1176//1000 1157//1001 -f 1058//996 1027//925 1059//927 -f 1058//996 1059//927 1094//1002 -f 1093//997 1058//996 1094//1002 -f 1093//997 1094//1002 1127//1003 -f 1126//998 1093//997 1127//1003 -f 1126//998 1127//1003 1160//1004 -f 1159//999 1126//998 1160//1004 -f 1159//999 1160//1004 1192//1005 -f 1176//1000 1159//999 1192//1005 -f 1176//1000 1192//1005 1189//1006 -f 1157//1001 1176//1000 1189//1006 -f 1157//1001 1189//1006 1178//1007 -f 1094//1002 1059//927 1095//929 -f 1094//1002 1095//929 1128//1008 -f 1127//1003 1094//1002 1128//1008 -f 1127//1003 1128//1008 1161//1009 -f 1160//1004 1127//1003 1161//1009 -f 1160//1004 1161//1009 1193//1010 -f 1192//1005 1160//1004 1193//1010 -f 1192//1005 1193//1010 1215//1011 -f 1189//1006 1192//1005 1215//1011 -f 1189//1006 1215//1011 1207//1012 -f 1178//1007 1189//1006 1207//1012 -f 1178//1007 1207//1012 1191//1013 -f 1128//1008 1095//929 1129//931 -f 1128//1008 1129//931 1162//1014 -f 1161//1009 1128//1008 1162//1014 -f 1161//1009 1162//1014 1194//1015 -f 1193//1010 1161//1009 1194//1015 -f 1193//1010 1194//1015 1217//1016 -f 1215//1011 1193//1010 1217//1016 -f 1215//1011 1217//1016 1227//1017 -f 1207//1012 1215//1011 1227//1017 -f 1207//1012 1227//1017 1216//1018 -f 1191//1013 1207//1012 1216//1018 -f 1191//1013 1216//1018 1209//1019 -f 1162//1014 1129//931 1163//933 -f 1162//1014 1163//933 1195//1020 -f 1194//1015 1162//1014 1195//1020 -f 1194//1015 1195//1020 1218//1021 -f 1217//1016 1194//1015 1218//1021 -f 1217//1016 1218//1021 1230//1022 -f 1227//1017 1217//1016 1230//1022 -f 1227//1017 1230//1022 1229//1023 -f 1216//1018 1227//1017 1229//1023 -f 1216//1018 1229//1023 1225//1024 -f 1209//1019 1216//1018 1225//1024 -f 1209//1019 1225//1024 1203//1025 -f 1195//1020 1163//933 1196//935 -f 1195//1020 1196//935 1219//1026 -f 1218//1021 1195//1020 1219//1026 -f 1218//1021 1219//1026 1231//1027 -f 1230//1022 1218//1021 1231//1027 -f 1230//1022 1231//1027 1232//1028 -f 1229//1023 1230//1022 1232//1028 -f 1229//1023 1232//1028 1228//1029 -f 1225//1024 1229//1023 1228//1029 -f 1225//1024 1228//1029 1214//1030 -f 1203//1025 1225//1024 1214//1030 -f 1203//1025 1214//1030 1184//1031 -f 1219//1026 1196//935 1220//937 -f 1219//1026 1220//937 1224//1032 -f 1231//1027 1219//1026 1224//1032 -f 1231//1027 1224//1032 1223//1033 -f 1232//1028 1231//1027 1223//1033 -f 1232//1028 1223//1033 1222//1034 -f 1228//1029 1232//1028 1222//1034 -f 1228//1029 1222//1034 1221//1035 -f 1214//1030 1228//1029 1221//1035 -f 1214//1030 1221//1035 1204//1036 -f 1184//1031 1214//1030 1204//1036 -f 1184//1031 1204//1036 1171//1037 -f 1224//1032 1220//937 1201//939 -f 1224//1032 1201//939 1200//1038 -f 1223//1033 1224//1032 1200//1038 -f 1223//1033 1200//1038 1199//1039 -f 1222//1034 1223//1033 1199//1039 -f 1222//1034 1199//1039 1198//1040 -f 1221//1035 1222//1034 1198//1040 -f 1221//1035 1198//1040 1197//1041 -f 1204//1036 1221//1035 1197//1041 -f 1204//1036 1197//1041 1185//1042 -f 1171//1037 1204//1036 1185//1042 -f 1171//1037 1185//1042 1151//1043 -f 1200//1038 1201//939 1169//941 -f 1200//1038 1169//941 1168//1044 -f 1199//1039 1200//1038 1168//1044 -f 1199//1039 1168//1044 1167//1045 -f 1198//1040 1199//1039 1167//1045 -f 1198//1040 1167//1045 1166//1046 -f 1197//1041 1198//1040 1166//1046 -f 1197//1041 1166//1046 1165//1047 -f 1185//1042 1197//1041 1165//1047 -f 1185//1042 1165//1047 1164//1048 -f 1151//1043 1185//1042 1164//1048 -f 1151//1043 1164//1048 1137//1049 -f 1168//1044 1169//941 1135//943 -f 1168//1044 1135//943 1134//1050 -f 1167//1045 1168//1044 1134//1050 -f 1167//1045 1134//1050 1133//1051 -f 1166//1046 1167//1045 1133//1051 -f 1166//1046 1133//1051 1132//1052 -f 1165//1047 1166//1046 1132//1052 -f 1165//1047 1132//1052 1131//1053 -f 1164//1048 1165//1047 1131//1053 -f 1164//1048 1131//1053 1130//1054 -f 1137//1049 1164//1048 1130//1054 -f 1137//1049 1130//1054 1117//1055 -f 1134//1050 1135//943 1102//945 -f 1134//1050 1102//945 1101//1056 -f 1133//1051 1134//1050 1101//1056 -f 1133//1051 1101//1056 1100//1057 -f 1132//1052 1133//1051 1100//1057 -f 1132//1052 1100//1057 1099//1058 -f 1131//1053 1132//1052 1099//1058 -f 1131//1053 1099//1058 1098//1059 -f 1130//1054 1131//1053 1098//1059 -f 1130//1054 1098//1059 1097//1060 -f 1117//1055 1130//1054 1097//1060 -f 1117//1055 1097//1060 1096//1061 -f 1101//1056 1102//945 1065//947 -f 1101//1056 1065//947 1064//1062 -f 1100//1057 1101//1056 1064//1062 -f 1100//1057 1064//1062 1063//1063 -f 1099//1058 1100//1057 1063//1063 -f 1099//1058 1063//1063 1062//1064 -f 1098//1059 1099//1058 1062//1064 -f 1098//1059 1062//1064 1061//1065 -f 1097//1060 1098//1059 1061//1065 -f 1097//1060 1061//1065 1060//1066 -f 1096//1061 1097//1060 1060//1066 -f 1096//1061 1060//1066 1066//1067 -f 1064//1062 1065//947 1032//949 -f 1064//1062 1032//949 1031//1068 -f 1063//1063 1064//1062 1031//1068 -f 1063//1063 1031//1068 1030//1069 -f 1062//1064 1063//1063 1030//1069 -f 1062//1064 1030//1069 1029//1070 -f 1061//1065 1062//1064 1029//1070 -f 1061//1065 1029//1070 1028//1071 -f 1060//1066 1061//1065 1028//1071 -f 1060//1066 1028//1071 1033//1072 -f 1066//1067 1060//1066 1033//1072 -f 1066//1067 1033//1072 1050//1073 -f 1031//1068 1032//949 1003//951 -f 1031//1068 1003//951 1002//1074 -f 1030//1069 1031//1068 1002//1074 -f 1030//1069 1002//1074 1001//1075 -f 1029//1070 1030//1069 1001//1075 -f 1029//1070 1001//1075 1000//1076 -f 1028//1071 1029//1070 1000//1076 -f 1028//1071 1000//1076 1004//1077 -f 1033//1072 1028//1071 1004//1077 -f 1033//1072 1004//1077 1021//1078 -f 1050//1073 1033//1072 1021//1078 -f 1050//1073 1021//1078 1041//1079 -f 1002//1074 1003//951 974//953 -f 1002//1074 974//953 973//1080 -f 1001//1075 1002//1074 973//1080 -f 1001//1075 973//1080 972//1081 -f 1000//1076 1001//1075 972//1081 -f 1000//1076 972//1081 975//1082 -f 1004//1077 1000//1076 975//1082 -f 1004//1077 975//1082 992//1083 -f 1021//1078 1004//1077 992//1083 -f 1021//1078 992//1083 1010//1084 -f 1041//1079 1021//1078 1010//1084 -f 1041//1079 1010//1084 1040//1085 -f 973//1080 974//953 951//955 -f 973//1080 951//955 950//1086 -f 972//1081 973//1080 950//1086 -f 972//1081 950//1086 952//1087 -f 975//1082 972//1081 952//1087 -f 975//1082 952//1087 965//1088 -f 992//1083 975//1082 965//1088 -f 992//1083 965//1088 983//1089 -f 1010//1084 992//1083 983//1089 -f 1010//1084 983//1089 1009//1090 -f 1040//1085 1010//1084 1009//1090 -f 1040//1085 1009//1090 1039//1091 -f 950//1086 951//955 934//957 -f 950//1086 934//957 935//1092 -f 952//1087 950//1086 935//1092 -f 952//1087 935//1092 944//1093 -f 965//1088 952//1087 944//1093 -f 965//1088 944//1093 958//1094 -f 983//1089 965//1088 958//1094 -f 983//1089 958//1094 982//1095 -f 1009//1090 983//1089 982//1095 -f 1009//1090 982//1095 1008//1096 -f 1039//1091 1009//1090 1008//1096 -f 1039//1091 1008//1096 1038//1097 -f 935//1092 934//957 924//959 -f 935//1092 924//959 929//1098 -f 944//1093 935//1092 929//1098 -f 944//1093 929//1098 939//1099 -f 958//1094 944//1093 939//1099 -f 958//1094 939//1099 957//1100 -f 982//1095 958//1094 957//1100 -f 982//1095 957//1100 981//1101 -f 1008//1096 982//1095 981//1101 -f 1008//1096 981//1101 1007//1102 -f 1038//1097 1008//1096 1007//1102 -f 1038//1097 1007//1102 1037//1103 -f 924//959 920//912 929//1098 -f 929//1098 920//912 926//960 -f 939//1099 929//1098 926//960 -f 939//1099 926//960 938//962 -f 957//1100 939//1099 938//962 -f 957//1100 938//962 956//964 -f 981//1101 957//1100 956//964 -f 981//1101 956//964 980//966 -f 1007//1102 981//1101 980//966 -f 1007//1102 980//966 1006//968 -f 1037//1103 1007//1102 1006//968 -f 1037//1103 1006//968 1036//970 -f 993//1104 1005//1105 976//1106 -f 966//1107 993//1104 976//1106 -f 966//1107 976//1106 953//1108 -f 945//1109 966//1107 953//1108 -f 945//1109 953//1108 936//1110 -f 930//1111 945//1109 936//1110 -f 930//1111 936//1110 925//1112 -f 921//913 930//1111 925//1112 -f 921//913 925//1112 922//914 -f 976//1106 1005//1105 977//1113 -f 953//1108 976//1106 977//1113 -f 953//1108 977//1113 954//1114 -f 936//1110 953//1108 954//1114 -f 936//1110 954//1114 937//1115 -f 925//1112 936//1110 937//1115 -f 925//1112 937//1115 931//1116 -f 922//914 925//1112 931//1116 -f 922//914 931//1116 927//916 -f 977//1113 1005//1105 978//1117 -f 954//1114 977//1113 978//1117 -f 954//1114 978//1117 955//1118 -f 937//1115 954//1114 955//1118 -f 937//1115 955//1118 946//1119 -f 931//1116 937//1115 946//1119 -f 931//1116 946//1119 941//1120 -f 927//916 931//1116 941//1120 -f 927//916 941//1120 940//918 -f 978//1117 1005//1105 979//1121 -f 955//1118 978//1117 979//1121 -f 955//1118 979//1121 967//1122 -f 946//1119 955//1118 967//1122 -f 946//1119 967//1122 961//1123 -f 941//1120 946//1119 961//1123 -f 941//1120 961//1123 960//1124 -f 940//918 941//1120 960//1124 -f 940//918 960//1124 959//920 -f 979//1121 1005//1105 994//1125 -f 967//1122 979//1121 994//1125 -f 967//1122 994//1125 987//1126 -f 961//1123 967//1122 987//1126 -f 961//1123 987//1126 986//1127 -f 960//1124 961//1123 986//1127 -f 960//1124 986//1127 985//1128 -f 959//920 960//1124 985//1128 -f 959//920 985//1128 984//922 -f 994//1125 1005//1105 1015//1129 -f 987//1126 994//1125 1015//1129 -f 987//1126 1015//1129 1014//1130 -f 986//1127 987//1126 1014//1130 -f 986//1127 1014//1130 1013//1131 -f 985//1128 986//1127 1013//1131 -f 985//1128 1013//1131 1012//1132 -f 984//922 985//1128 1012//1132 -f 984//922 1012//1132 1011//924 -f 1015//1129 1005//1105 1035//1133 -f 1014//1130 1015//1129 1035//1133 -f 1014//1130 1035//1133 1045//1134 -f 1013//1131 1014//1130 1045//1134 -f 1013//1131 1045//1134 1044//1135 -f 1012//1132 1013//1131 1044//1135 -f 1012//1132 1044//1135 1043//1136 -f 1011//924 1012//1132 1043//1136 -f 1011//924 1043//1136 1042//926 -f 1035//1133 1005//1105 1052//1137 -f 1045//1134 1035//1133 1052//1137 -f 1045//1134 1052//1137 1080//1138 -f 1044//1135 1045//1134 1080//1138 -f 1044//1135 1080//1138 1079//1139 -f 1043//1136 1044//1135 1079//1139 -f 1043//1136 1079//1139 1078//1140 -f 1042//926 1043//1136 1078//1140 -f 1042//926 1078//1140 1077//928 -f 1052//1137 1005//1105 1068//1141 -f 1080//1138 1052//1137 1068//1141 -f 1080//1138 1068//1141 1106//1142 -f 1079//1139 1080//1138 1106//1142 -f 1079//1139 1106//1142 1112//1143 -f 1078//1140 1079//1139 1112//1143 -f 1078//1140 1112//1143 1111//1144 -f 1077//928 1078//1140 1111//1144 -f 1077//928 1111//1144 1110//930 -f 1068//1141 1005//1105 1087//1145 -f 1106//1142 1068//1141 1087//1145 -f 1106//1142 1087//1145 1120//1146 -f 1112//1143 1106//1142 1120//1146 -f 1112//1143 1120//1146 1146//1147 -f 1111//1144 1112//1143 1146//1147 -f 1111//1144 1146//1147 1145//1148 -f 1110//930 1111//1144 1145//1148 -f 1110//930 1145//1148 1144//932 -f 1087//1145 1005//1105 1107//1149 -f 1120//1146 1087//1145 1107//1149 -f 1120//1146 1107//1149 1140//1150 -f 1146//1147 1120//1146 1140//1150 -f 1146//1147 1140//1150 1174//1151 -f 1145//1148 1146//1147 1174//1151 -f 1145//1148 1174//1151 1180//1152 -f 1144//932 1145//1148 1180//1152 -f 1144//932 1180//1152 1179//934 -f 1107//1149 1005//1105 1121//1153 -f 1140//1150 1107//1149 1121//1153 -f 1140//1150 1121//1153 1154//1154 -f 1174//1151 1140//1150 1154//1154 -f 1174//1151 1154//1154 1188//1155 -f 1180//1152 1174//1151 1188//1155 -f 1180//1152 1188//1155 1211//1156 -f 1179//934 1180//1152 1211//1156 -f 1179//934 1211//1156 1210//936 -f 1121//1153 1005//1105 1141//1157 -f 1154//1154 1121//1153 1141//1157 -f 1154//1154 1141//1157 1175//1158 -f 1188//1155 1154//1154 1175//1158 -f 1188//1155 1175//1158 1206//1159 -f 1211//1156 1188//1155 1206//1159 -f 1211//1156 1206//1159 1226//1160 -f 1210//936 1211//1156 1226//1160 -f 1210//936 1226//1160 1212//938 -f 1141//1157 1005//1105 1155//1161 -f 1175//1158 1141//1157 1155//1161 -f 1175//1158 1155//1161 1187//1162 -f 1206//1159 1175//1158 1187//1162 -f 1206//1159 1187//1162 1205//1163 -f 1226//1160 1206//1159 1205//1163 -f 1226//1160 1205//1163 1213//1164 -f 1212//938 1226//1160 1213//1164 -f 1212//938 1213//1164 1181//940 -f 1155//1161 1005//1105 1153//1165 -f 1187//1162 1155//1161 1153//1165 -f 1187//1162 1153//1165 1173//1166 -f 1205//1163 1187//1162 1173//1166 -f 1205//1163 1173//1166 1186//1167 -f 1213//1164 1205//1163 1186//1167 -f 1213//1164 1186//1167 1182//1168 -f 1181//940 1213//1164 1182//1168 -f 1181//940 1182//1168 1147//942 -f 1153//1165 1005//1105 1139//1169 -f 1173//1166 1153//1165 1139//1169 -f 1173//1166 1139//1169 1152//1170 -f 1186//1167 1173//1166 1152//1170 -f 1186//1167 1152//1170 1172//1171 -f 1182//1168 1186//1167 1172//1171 -f 1182//1168 1172//1171 1148//1172 -f 1147//942 1182//1168 1148//1172 -f 1147//942 1148//1172 1113//944 -f 1139//1169 1005//1105 1119//1173 -f 1152//1170 1139//1169 1119//1173 -f 1152//1170 1119//1173 1138//1174 -f 1172//1171 1152//1170 1138//1174 -f 1172//1171 1138//1174 1149//1175 -f 1148//1172 1172//1171 1149//1175 -f 1148//1172 1149//1175 1114//1176 -f 1113//944 1148//1172 1114//1176 -f 1113//944 1114//1176 1081//946 -f 1119//1173 1005//1105 1105//1177 -f 1138//1174 1119//1173 1105//1177 -f 1138//1174 1105//1177 1118//1178 -f 1149//1175 1138//1174 1118//1178 -f 1149//1175 1118//1178 1115//1179 -f 1114//1176 1149//1175 1115//1179 -f 1114//1176 1115//1179 1082//1180 -f 1081//946 1114//1176 1082//1180 -f 1081//946 1082//1180 1046//948 -f 1105//1177 1005//1105 1086//1181 -f 1118//1178 1105//1177 1086//1181 -f 1118//1178 1086//1181 1104//1182 -f 1115//1179 1118//1178 1104//1182 -f 1115//1179 1104//1182 1083//1183 -f 1082//1180 1115//1179 1083//1183 -f 1082//1180 1083//1183 1047//1184 -f 1046//948 1082//1180 1047//1184 -f 1046//948 1047//1184 1016//950 -f 1086//1181 1005//1105 1067//1185 -f 1104//1182 1086//1181 1067//1185 -f 1104//1182 1067//1185 1084//1186 -f 1083//1183 1104//1182 1084//1186 -f 1083//1183 1084//1186 1048//1187 -f 1047//1184 1083//1183 1048//1187 -f 1047//1184 1048//1187 1017//1188 -f 1016//950 1047//1184 1017//1188 -f 1016//950 1017//1188 988//952 -f 1067//1185 1005//1105 1051//1189 -f 1084//1186 1067//1185 1051//1189 -f 1084//1186 1051//1189 1049//1190 -f 1048//1187 1084//1186 1049//1190 -f 1048//1187 1049//1190 1018//1191 -f 1017//1188 1048//1187 1018//1191 -f 1017//1188 1018//1191 989//1192 -f 988//952 1017//1188 989//1192 -f 988//952 989//1192 962//954 -f 1051//1189 1005//1105 1034//1193 -f 1049//1190 1051//1189 1034//1193 -f 1049//1190 1034//1193 1019//1194 -f 1018//1191 1049//1190 1019//1194 -f 1018//1191 1019//1194 990//1195 -f 989//1192 1018//1191 990//1195 -f 989//1192 990//1195 963//1196 -f 962//954 989//1192 963//1196 -f 962//954 963//1196 942//956 -f 1034//1193 1005//1105 1020//1197 -f 1019//1194 1034//1193 1020//1197 -f 1019//1194 1020//1197 991//1198 -f 990//1195 1019//1194 991//1198 -f 990//1195 991//1198 964//1199 -f 963//1196 990//1195 964//1199 -f 963//1196 964//1199 943//1200 -f 942//956 963//1196 943//1200 -f 942//956 943//1200 928//958 -f 1020//1197 1005//1105 993//1104 -f 991//1198 1020//1197 993//1104 -f 991//1198 993//1104 966//1107 -f 964//1199 991//1198 966//1107 -f 964//1199 966//1107 945//1109 -f 943//1200 964//1199 945//1109 -f 943//1200 945//1109 930//1111 -f 928//958 943//1200 930//1111 -f 928//958 930//1111 921//913 -f 1151//1043 1137//1049 1150//1201 -f 1142//995 1143//1202 1122//989 -f 1053//971 1089//977 1069//1203 -f 1122//989 1123//1204 1109//983 -f 1096//1061 1103//1205 1117//1055 -f 1171//1037 1170//1206 1184//1031 -f 1191//1013 1209//1019 1190//1207 -f 1036//970 1053//971 1070//1208 -f 1036//970 1070//1208 1037//1103 -f 1203//1025 1202//1209 1209//1019 -f 1066//1067 1085//1210 1096//1061 -f 1050//1073 1076//1211 1066//1067 -f 1184//1031 1183//1212 1203//1025 -f 1041//1079 1040//1085 1075//1213 -f 1171//1037 1151//1043 1170//1206 -f 1157//1001 1158//1214 1142//995 -f 1137//1049 1117//1055 1136//1215 -f 1038//1097 1072//1216 1039//1091 -f 1178//1007 1191//1013 1177//1217 -f 1089//977 1109//983 1088//1218 -f 1037//1103 1071//1219 1038//1097 -f 1050//1073 1041//1079 1076//1211 -f 1157//1001 1178//1007 1158//1214 -f 1040//1085 1039//1091 1074//1220 -f 1170//1206 1150//1201 452//1221 -f 1264//1222 1071//1219 1263//1223 -f 1261//1224 1262//1225 1076//1211 -f 1085//1210 1076//1211 1262//1225 -f 1190//1207 1208//1226 405//1227 -f 509//176 526//519 1260//1228 -f 1260//1228 526//519 556//523 -f 167//201 556//523 595//202 -f 595//202 621//526 1255//200 -f 172//192 163//182 1254//199 -f 8//49 18//42 3//1229 -f 3//1229 18//42 1288//1230 -f 15//75 7//66 1240//82 -f 1250//50 8//49 3//1229 -f 1//32 1283//40 566//33 -f 593//3 566//33 9//41 -f 19//1231 20//34 33//25 -f 1243//2 636//89 593//3 -f 64//18 40//6 36//17 -f 1244//24 64//18 36//17 -f 641//579 614//582 1237//1232 -f 1250//50 1239//1233 4//48 -f 7//66 4//48 1239//1233 -f 5//74 14//73 11//81 -f 1240//82 7//66 1239//1233 -f 1//32 553//31 565//56 -f 593//3 9//41 1289//83 -f 18//42 20//34 1288//1230 -f 40//6 27//5 1252//16 -f 1249//58 6//65 565//56 -f 14//73 15//75 11//81 -f 592//57 1234//130 1233//58 -f 1237//1232 614//582 588//586 -f 1237//1232 588//586 605//592 -f 539//175 159//177 1259//1234 -f 164//257 1257//1235 168//251 -f 509//176 1256//1236 159//177 -f 1258//250 585//249 1259//1234 -f 169//1237 164//257 174//242 -f 191//236 208//230 185//1238 -f 1290//1239 207//229 640//777 -f 621//526 1292//1240 1255//200 -f 163//182 160//178 157//180 -f 1284//1241 1292//1240 648//227 -f 1252//16 27//5 12//95 -f 119//124 116//129 104//161 -f 116//129 88//135 1242//136 -f 87//159 666//713 715//160 -f 87//159 1241//1242 666//713 -f 1253//1243 47//150 631//152 -f 631//152 666//713 1253//1243 -f 86//142 74//144 60//143 -f 640//777 1291//1244 1290//1239 -f 1291//1244 640//777 613//248 -f 208//230 219//220 200//1245 -f 665//778 640//777 207//229 -f 1285//226 200//1245 219//220 -f 648//227 207//229 1284//1241 -f 691//228 665//778 207//229 -f 605//592 1236//1246 1245//1246 -f 1235//151 605//592 631//152 -f 1248//145 48//153 37//167 -f 667//708 1251//1247 1247//158 -f 605//592 1235//151 1236//1246 -f 641//579 1238//1248 1246//1249 -f 641//579 1237//1232 1238//1248 -f 404//412 391//405 1267//904 -f 1265//911 453//433 436//426 -f 328//351 327//350 331//910 -f 443//445 462//439 469//1250 -f 342//370 1275//1251 344//1252 -f 342//370 334//363 1275//1251 -f 372//480 385//473 384//906 -f 351//377 1271//1253 361//384 -f 351//377 350//1254 1271//1253 -f 337//1255 335//508 345//501 -f 384//906 385//473 396//466 -f 361//384 368//1256 369//391 -f 361//384 1271//1253 368//1256 -f 352//494 1276//1257 346//908 -f 397//1258 396//466 411//459 -f 342//370 350//1254 351//377 -f 342//370 344//1252 350//1254 -f 380//398 369//391 368//1256 -f 334//363 328//351 332//1259 -f 404//412 418//1260 419//419 -f 404//412 1268//905 418//1260 -f 426//452 1286//1261 1282//1262 -f 1282//1262 411//459 426//452 -f 352//494 362//487 1277//1263 -f 1277//1263 1276//1257 352//494 -f 391//405 380//398 381//1264 -f 426//452 443//445 444//1265 -f 444//1265 1286//1261 426//452 -f 362//487 372//480 373//907 -f 373//907 1277//1263 362//487 -f 1202//1209 1183//1212 1270//1266 -f 1116//1267 428//1268 1136//1215 -f 428//1268 1287//1269 1136//1215 -f 1073//1270 353//1271 1074//1220 -f 353//1271 363//1272 1074//1220 -f 353//1271 1073//1270 1279//1273 -f 1070//1208 1069//1203 1263//1223 -f 1274//1274 1069//1203 1088//1218 -f 1177//1217 1273//1275 1272//1276 -f 1272//1276 1158//1214 1177//1217 -f 428//1268 1116//1267 427//1277 -f 1088//1218 1108//1278 343//1279 -f 1103//1205 1085//1210 1278//1280 -f 1108//1278 1123//1204 1280//1281 -f 1123//1204 1281//1282 1280//1281 -f 1143//1202 1158//1214 360//1283 -f 1158//1214 1272//1276 360//1283 -f 1075//1213 1261//1224 1076//1211 -f 1085//1210 1262//1225 1278//1280 -f 1123//1204 1143//1202 1281//1282 -f 1143//1202 360//1283 1281//1282 -f 1074//1220 363//1272 1075//1213 -f 363//1272 1261//1224 1075//1213 -f 1270//1266 1183//1212 452//1221 -f 1266//1284 1150//1201 1136//1215 -f 1273//1275 1177//1217 1190//1207 -f 1208//1226 1202//1209 406//1285 -f 1202//1209 1269//1286 406//1285 -f 1072//1216 1071//1219 1264//1222 -f 1241//1242 1253//1243 666//713 -f 71//137 86//142 60//143 -f 88//135 86//142 71//137 -f 1245//1246 1237//1232 605//592 -f 119//124 104//161 90//119 -f 667//708 1246//1249 1251//1247 -f 621//526 648//227 1292//1240 -f 208//230 200//1245 185//1238 -f 174//242 191//236 169//1237 -f 1258//250 1291//1244 613//248 -f 1244//24 19//1231 33//25 -f 1288//1230 20//34 19//1231 -f 199//208 182//198 178//1287 -f 206//1288 218//214 190//1289 -f 1260//1228 556//523 167//201 -f 157//180 1254//199 163//182 -f 178//1287 182//198 1254//199 -f 218//214 206//1288 1285//226 -f 161//258 1257//1235 158//179 -f 324//345 312//274 323//336 -f 303//317 302//323 316//338 -f 305//305 304//311 318//340 -f 304//311 303//317 317//339 -f 322//347 309//281 321//341 -f 320//346 307//293 319//342 -f 301//329 311//335 314//344 -f 302//323 301//329 315//337 -f 311//335 312//274 313//343 -f 319//342 306//299 318//340 -f 321//341 308//287 320//346 -f 323//336 310//275 322//347 -f 418//1260 435//909 419//419 -f 337//1255 331//910 335//508 -f 469//1250 462//439 1265//911 -f 1137//1049 1136//1215 1150//1201 -f 1143//1202 1123//1204 1122//989 -f 1089//977 1088//1218 1069//1203 -f 1123//1204 1108//1278 1109//983 -f 1103//1205 1116//1267 1117//1055 -f 1170//1206 1183//1212 1184//1031 -f 1209//1019 1208//1226 1190//1207 -f 1053//971 1069//1203 1070//1208 -f 1070//1208 1071//1219 1037//1103 -f 1202//1209 1208//1226 1209//1019 -f 1085//1210 1103//1205 1096//1061 -f 1076//1211 1085//1210 1066//1067 -f 1183//1212 1202//1209 1203//1025 -f 1040//1085 1074//1220 1075//1213 -f 1151//1043 1150//1201 1170//1206 -f 1158//1214 1143//1202 1142//995 -f 1117//1055 1116//1267 1136//1215 -f 1072//1216 1073//1270 1039//1091 -f 1191//1013 1190//1207 1177//1217 -f 1109//983 1108//1278 1088//1218 -f 1071//1219 1072//1216 1038//1097 -f 1041//1079 1075//1213 1076//1211 -f 1178//1007 1177//1217 1158//1214 -f 1039//1091 1073//1270 1074//1220 -f 1150//1201 1266//1284 452//1221 -f 1071//1219 1070//1208 1263//1223 -f 1208//1226 406//1285 405//1227 -f 1256//1236 509//176 1260//1228 -f 1257//1235 161//258 168//251 -f 585//249 539//175 1259//1234 -f 715//160 667//708 1247//158 -f 667//708 641//579 1246//1249 -f 435//909 1265//911 436//426 -f 332//1259 328//351 331//910 -f 444//1265 443//445 469//1250 -f 346//908 337//1255 345//501 -f 397//1258 384//906 396//466 -f 1282//1262 397//1258 411//459 -f 381//1264 380//398 368//1256 -f 1275//1251 334//363 332//1259 -f 1267//904 391//405 381//1264 -f 1269//1286 1202//1209 1270//1266 -f 1073//1270 1072//1216 1279//1273 -f 1069//1203 1274//1274 1263//1223 -f 343//1279 1274//1274 1088//1218 -f 1116//1267 1103//1205 427//1277 -f 1108//1278 1280//1281 343//1279 -f 427//1277 1103//1205 1278//1280 -f 1183//1212 1170//1206 452//1221 -f 1287//1269 1266//1284 1136//1215 -f 405//1227 1273//1275 1190//1207 -f 1279//1273 1072//1216 1264//1222 -f 191//236 185//1238 169//1237 -f 190//1289 199//208 178//1287 -f 218//214 199//208 190//1289 diff --git a/Samples/Core/ShaderBuffers/ShaderBuffers.cpp b/Samples/Core/ShaderBuffers/ShaderBuffers.cpp deleted file mode 100644 index a28ac1e3e..000000000 --- a/Samples/Core/ShaderBuffers/ShaderBuffers.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "ShaderBuffers.h" - -const std::string ShaderBuffersSample::skDefaultModel = "teapot.obj"; - -void ShaderBuffersSample::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - pGui->addDirectionWidget("Light Direction", mLightData.worldDir); - pGui->addRgbColor("Light intensity", mLightData.intensity); - pGui->addRgbColor("Surface Color", mSurfaceColor); - pGui->addCheckBox("Count FS invocations", mCountPixelShaderInvocations); -} - -Vao::SharedConstPtr ShaderBuffersSample::getVao() -{ - auto pMesh = mpModel->getMesh(0); - auto pVao = pMesh->getVao(); - return pVao; -} - -void ShaderBuffersSample::onLoad(SampleCallbacks* pSample, RenderContext* pContext) -{ - mpCamera = Camera::create(); - - // create the program - mpProgram = GraphicsProgram::createFromFile("ShaderBuffers.hlsl", "vs", "ps"); - - // Load the model - mpModel = Model::createFromFile(skDefaultModel.c_str()); - - // Plane has only one mesh, get the VAO now - mpVao = getVao(); - auto pMesh = mpModel->getMesh(0); - mIndexCount = pMesh->getIndexCount(); - - // Set camera parameters - glm::vec3 center = mpModel->getCenter(); - float radius = mpModel->getRadius(); - - float nearZ = 0.1f; - float farZ = radius * 100; - mpCamera->setDepthRange(nearZ, farZ); - - // Initialize the camera controller - mCameraController.attachCamera(mpCamera); - mCameraController.setModelParams(center, radius, radius * 2.5f); - - // create the uniform buffers - mpProgramVars = GraphicsVars::create(mpProgram->getReflector()); - mpSurfaceColorBuffer = TypedBuffer::create(1); - uint32_t z = 0; - mpInvocationsBuffer = Buffer::create(sizeof(uint32_t), Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::Read, &z); - mpProgramVars->setRawBuffer("gInvocationBuffer", mpInvocationsBuffer); - mpProgramVars->setTypedBuffer("gSurfaceColor[1]", mpSurfaceColorBuffer); - - mpRWBuffer = StructuredBuffer::create(mpProgram, "gRWBuffer", 4); - mpProgramVars->setStructuredBuffer("gRWBuffer", mpRWBuffer); - - // create pipeline cache - mpGraphicsState = GraphicsState::create(); - RasterizerState::Desc rsDesc; - rsDesc.setCullMode(RasterizerState::CullMode::Back); - mpGraphicsState->setRasterizerState(RasterizerState::create(rsDesc)); - - // Depth test - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(true); - mpGraphicsState->setDepthStencilState(DepthStencilState::create(dsDesc)); - mpGraphicsState->setVao(mpVao); - mpGraphicsState->setProgram(mpProgram); - - // Compute - mpComputeProgram = ComputeProgram::createFromFile("ShaderBuffers.cs.hlsl", "main"); - mpComputeState = ComputeState::create(); - mpComputeState->setProgram(mpComputeProgram); - - mpComputeVars = ComputeVars::create(mpComputeProgram->getReflector()); - mpComputeVars->setStructuredBuffer("gLightIn", StructuredBuffer::create(mpComputeProgram, "gLightIn", 2)); - - mpAppendLightData = StructuredBuffer::create(mpComputeProgram, "gLightOut", 2); - mpComputeVars->setStructuredBuffer("gLightOut", mpAppendLightData); -} - -void ShaderBuffersSample::onFrameRender(SampleCallbacks* pSample, RenderContext* pContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - mCameraController.update(); - - // - // Compute - // - - pContext->clearUAV(mpAppendLightData->getUAV().get(), uvec4(0)); - pContext->clearUAVCounter(mpAppendLightData, 0); - - // Send lights to compute shader - mpComputeVars->getStructuredBuffer("gLightIn")[0]["vec3Val"] = mLightData.worldDir; - mpComputeVars->getStructuredBuffer("gLightIn")[1]["vec3Val"] = mLightData.intensity; - mpComputeVars->setStructuredBuffer("gLightOut", mpAppendLightData); - - pContext->setComputeState(mpComputeState); - pContext->setComputeVars(mpComputeVars); - - // Compute shader passes light data through an append buffer - pContext->dispatch(1, 1, 1); - - // - // Render - // - - // Bind compute output - mpProgramVars->setStructuredBuffer("gLight[3]", mpAppendLightData); - mpGraphicsState->setFbo(pSample->getCurrentFbo()); - pContext->setGraphicsState(mpGraphicsState); - - // Update uniform-buffers data - mpProgramVars["PerFrameCB"]["gWorldMat"] = glm::mat4(); - glm::mat4 wvp = mpCamera->getViewProjMatrix(); - mpProgramVars["PerFrameCB"]["gWvpMat"] = wvp; - - mpSurfaceColorBuffer[0] = mSurfaceColor; - mpSurfaceColorBuffer->uploadToGPU(); - - // Set uniform buffers - pContext->setGraphicsVars(mpProgramVars); - pContext->drawIndexed(mIndexCount, 0, 0); - - // Read UAV counter from append buffer - uint32_t* pCounter = (uint32_t*)mpAppendLightData->getUAVCounter()->map(Buffer::MapType::Read); - std::string msg = "Light Data append buffer count: " + std::to_string(*pCounter); - pSample->renderText(msg, vec2(600, 80)); - mpAppendLightData->getUAVCounter()->unmap(); - - if(mCountPixelShaderInvocations) - { - // RWByteAddressBuffer - uint32_t* pData = (uint32_t*)mpInvocationsBuffer->map(Buffer::MapType::Read); - std::string msg = "PS was invoked " + std::to_string(*pData) + " times"; - pSample->renderText(msg, vec2(600, 100)); - mpInvocationsBuffer->unmap(); - - // RWStructuredBuffer UAV Counter - pData = (uint32_t*)mpRWBuffer->getUAVCounter()->map(Buffer::MapType::Read); - msg = "UAV Counter counted " + std::to_string(*pData) + " times"; - pSample->renderText(msg, vec2(600, 120)); - mpRWBuffer->getUAVCounter()->unmap(); - - pContext->clearUAV(mpInvocationsBuffer->getUAV().get(), uvec4(0)); - pContext->clearUAVCounter(mpRWBuffer, 0); - } -} - -void ShaderBuffersSample::onDataReload(SampleCallbacks* pSample) -{ - mpVao = getVao(); -} - -bool ShaderBuffersSample::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - return mCameraController.onKeyEvent(keyEvent); -} - -bool ShaderBuffersSample::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mCameraController.onMouseEvent(mouseEvent); -} - -void ShaderBuffersSample::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - mpCamera->setFocalLength(60.0f); - mpCamera->setAspectRatio((float)width / (float)height); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ -#ifdef FALCOR_VK - msgBox("Vulkan support for the features used in the Shader Buffers sample is coming soon!"); - return 0; -#endif - - ShaderBuffersSample::UniquePtr pRenderer = std::make_unique(); - - SampleConfig config; - config.windowDesc.title = "Shader Buffers"; - config.windowDesc.resizableWindow = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Core/ShaderBuffers/ShaderBuffers.h b/Samples/Core/ShaderBuffers/ShaderBuffers.h deleted file mode 100644 index 70add09f4..000000000 --- a/Samples/Core/ShaderBuffers/ShaderBuffers.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class ShaderBuffersSample : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - void onDataReload(SampleCallbacks* pSample) override; - -private: - GraphicsState::SharedPtr mpGraphicsState; - ComputeProgram::SharedPtr mpComputeProgram; - ComputeState::SharedPtr mpComputeState; - ComputeVars::SharedPtr mpComputeVars; - StructuredBuffer::SharedPtr mpLightBuffer; - - GraphicsProgram::SharedPtr mpProgram; - GraphicsVars::SharedPtr mpProgramVars; - Model::SharedPtr mpModel; - Vao::SharedConstPtr mpVao; - uint32_t mIndexCount = 0; - Buffer::SharedPtr mpInvocationsBuffer; - StructuredBuffer::SharedPtr mpRWBuffer; - StructuredBuffer::SharedPtr mpAppendLightData; - TypedBuffer::SharedPtr mpSurfaceColorBuffer; - - bool mCountPixelShaderInvocations = false; - - Camera::SharedPtr mpCamera; - ModelViewCameraController mCameraController; - - struct Light - { - glm::vec3 worldDir = glm::vec3(0, -1, 0); - glm::vec3 intensity = glm::vec3(0.6f, 0.8f, 0.8f); - }; - - Light mLightData; - - glm::vec3 mSurfaceColor = glm::vec3(0.36f,0.87f,0.52f); - Vao::SharedConstPtr getVao(); - - static const std::string skDefaultModel; -}; diff --git a/Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj.filters b/Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj.filters deleted file mode 100644 index e49648877..000000000 --- a/Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - Data - - - Data - - - - - {fa3410a7-25f7-42e1-b08c-7dfe082deadd} - - - \ No newline at end of file diff --git a/Samples/Core/SimpleDeferred/Data/LightingPass.ps.hlsl b/Samples/Core/SimpleDeferred/Data/LightingPass.ps.hlsl deleted file mode 100644 index d3c0af92b..000000000 --- a/Samples/Core/SimpleDeferred/Data/LightingPass.ps.hlsl +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -__import ShaderCommon; -__import Shading; - -cbuffer PerImageCB -{ - // G-Buffer - // Lighting params - LightData gDirLight; - LightData gPointLight; - float3 gAmbient; - // Debug mode - uint gDebugMode; -}; - -// Debug modes -#define ShowPos 1 -#define ShowNormals 2 -#define ShowAlbedo 3 -#define ShowLighting 4 - -float3 shade(float3 posW, float3 normalW, float linearRoughness, float4 albedo) -{ - // Discard empty pixels - if (albedo.a <= 0) - { - discard; - } - - /* Reconstruct the hit-point */ - ShadingData sd = initShadingData(); - sd.posW = posW; - sd.V = normalize(gCamera.posW - posW); - sd.N = normalW; - sd.NdotV = abs(dot(sd.V, sd.N)); - sd.linearRoughness = linearRoughness; - - /* Reconstruct layers (one diffuse layer) */ - sd.diffuse = albedo.rgb; - sd.opacity = 0; - - /* Do lighting */ - ShadingResult dirResult = evalMaterial(sd, gDirLight, 1); - ShadingResult pointResult = evalMaterial(sd, gPointLight, 1); - - float3 result; - // Debug vis - if (gDebugMode == ShowPos) - result = posW; - else if (gDebugMode == ShowNormals) - result = 0.5 * normalW + 0.5f; - else if (gDebugMode == ShowAlbedo) - result = albedo.rgb; - else if (gDebugMode == ShowLighting) - result = (dirResult.diffuseBrdf + pointResult.diffuseBrdf) / sd.diffuse.rgb; - else - result = dirResult.diffuse + pointResult.diffuse; - - return result; -} - -Texture2D gGBuf0; -Texture2D gGBuf1; -Texture2D gGBuf2; - -float4 main(float2 texC : TEXCOORD, float4 pos : SV_POSITION) : SV_TARGET -{ - // Fetch a G-Buffer - float3 posW = gGBuf0.Load(int3(pos.xy, 0)).rgb; - float4 buf1Val = gGBuf1.Load(int3(pos.xy, 0)); - float3 normalW = buf1Val.rgb; - float linearRoughness = buf1Val.a; - float4 albedo = gGBuf2.Load(int3(pos.xy, 0)); - - float3 color = shade(posW, normalW, linearRoughness, albedo); - - return float4(color, 1); -} diff --git a/Samples/Core/SimpleDeferred/SimpleDeferred.cpp b/Samples/Core/SimpleDeferred/SimpleDeferred.cpp deleted file mode 100644 index 6cc43eace..000000000 --- a/Samples/Core/SimpleDeferred/SimpleDeferred.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "SimpleDeferred.h" - -const std::string SimpleDeferred::skDefaultModel = "Arcade/Arcade.fbx"; - -SimpleDeferred::~SimpleDeferred() -{ -} - -CameraController& SimpleDeferred::getActiveCameraController() -{ - switch(mCameraType) - { - case SimpleDeferred::ModelViewCamera: - return mModelViewCameraController; - case SimpleDeferred::FirstPersonCamera: - return mFirstPersonCameraController; - default: - should_not_get_here(); - return mFirstPersonCameraController; - } -} - -void SimpleDeferred::loadModelFromFile(const std::string& filename, Fbo* pTargetFbo) -{ - Model::LoadFlags flags = Model::LoadFlags::None; - if (mGenerateTangentSpace == false) - { - flags |= Model::LoadFlags::DontGenerateTangentSpace; - } - auto fboFormat = pTargetFbo->getColorTexture(0)->getFormat(); - flags |= isSrgbFormat(fboFormat) ? Model::LoadFlags::None : Model::LoadFlags::AssumeLinearSpaceTextures; - - mpModel = Model::createFromFile(filename.c_str(), flags); - - if(mpModel == nullptr) - { - msgBox("Could not load model"); - return; - } - resetCamera(); - - float Radius = mpModel->getRadius(); - mpPointLight->setWorldPosition(glm::vec3(0, Radius*1.25f, 0)); -} - -void SimpleDeferred::loadModel(Fbo* pTargetFbo) -{ - std::string filename; - if(openFileDialog(Model::kFileExtensionFilters, filename)) - { - loadModelFromFile(filename, pTargetFbo); - } -} - -void SimpleDeferred::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - // Load model group - if (pGui->addButton("Load Model")) - { - loadModel(pSample->getCurrentFbo().get()); - } - - if(pGui->beginGroup("Load Options")) - { - pGui->addCheckBox("Generate Tangent Space", mGenerateTangentSpace); - pGui->endGroup(); - } - - Gui::DropdownList debugModeList; - debugModeList.push_back({ 0, "Disabled" }); - debugModeList.push_back({ 1, "Positions" }); - debugModeList.push_back({ 2, "Normals" }); - debugModeList.push_back({ 3, "Albedo" }); - debugModeList.push_back({ 4, "Illumination" }); - pGui->addDropdown("Debug mode", debugModeList, (uint32_t&)mDebugMode); - - Gui::DropdownList cullList; - cullList.push_back({0, "No Culling"}); - cullList.push_back({1, "Backface Culling"}); - cullList.push_back({2, "Frontface Culling"}); - pGui->addDropdown("Cull Mode", cullList, (uint32_t&)mCullMode); - - if(pGui->beginGroup("Lights")) - { - pGui->addRgbColor("Ambient intensity", mAmbientIntensity); - if(pGui->beginGroup("Directional Light")) - { - mpDirLight->renderUI(pGui); - pGui->endGroup(); - } - if (pGui->beginGroup("Point Light")) - { - mpPointLight->renderUI(pGui); - pGui->endGroup(); - } - pGui->endGroup(); - } - - Gui::DropdownList cameraList; - cameraList.push_back({ModelViewCamera, "Model-View"}); - cameraList.push_back({FirstPersonCamera, "First-Person"}); - pGui->addDropdown("Camera Type", cameraList, (uint32_t&)mCameraType); - - if (mpModel) - { - renderModelUiElements(pGui); - } -} - -void SimpleDeferred::renderModelUiElements(Gui* pGui) -{ - bool bAnim = mpModel->hasAnimations(); - static const char* animateStr = "Animate"; - static const char* activeAnimStr = "Active Animation"; - - if(bAnim) - { - mActiveAnimationID = sBindPoseAnimationID; - - pGui->addCheckBox(animateStr, mAnimate); - Gui::DropdownList list; - list.resize(mpModel->getAnimationsCount() + 1); - list[0].label = "Bind Pose"; - list[0].value = sBindPoseAnimationID; - - for(uint32_t i = 0; i < mpModel->getAnimationsCount(); i++) - { - list[i + 1].value = i; - list[i + 1].label = mpModel->getAnimationName(i); - if(list[i + 1].label.size() == 0) - { - list[i + 1].label = std::to_string(i); - } - } - if (pGui->addDropdown(activeAnimStr, list, mActiveAnimationID)) - { - mpModel->setActiveAnimation(mActiveAnimationID); - } - } - if(pGui->beginGroup("Depth Range")) - { - const float minDepth = mpModel->getRadius() * 1 / 1000; - pGui->addFloatVar("Near Plane", mNearZ, minDepth, mpModel->getRadius() * 15, minDepth * 5); - pGui->addFloatVar("Far Plane", mFarZ, minDepth, mpModel->getRadius() * 15, minDepth * 5); - pGui->endGroup(); - } -} - -void SimpleDeferred::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpCamera = Camera::create(); - - mpDeferredPassProgram = GraphicsProgram::createFromFile("DeferredPass.ps.hlsl", "", "main"); - - mpLightingPass = FullScreenPass::create("LightingPass.ps.hlsl"); - - // create rasterizer state - RasterizerState::Desc rsDesc; - mpCullRastState[0] = RasterizerState::create(rsDesc); - rsDesc.setCullMode(RasterizerState::CullMode::Back); - mpCullRastState[1] = RasterizerState::create(rsDesc); - rsDesc.setCullMode(RasterizerState::CullMode::Front); - mpCullRastState[2] = RasterizerState::create(rsDesc); - - // Depth test - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(false); - mpNoDepthDS = DepthStencilState::create(dsDesc); - dsDesc.setDepthTest(true); - mpDepthTestDS = DepthStencilState::create(dsDesc); - - // Blend state - BlendState::Desc blendDesc; - mpOpaqueBS = BlendState::create(blendDesc); - - mModelViewCameraController.attachCamera(mpCamera); - mFirstPersonCameraController.attachCamera(mpCamera); - - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear).setMaxAnisotropy(8); - mpLinearSampler = Sampler::create(samplerDesc); - - mpPointLight = PointLight::create(); - mpDirLight = DirectionalLight::create(); - mpDirLight->setWorldDirection(glm::vec3(-0.5f, -0.2f, -1.0f)); - - mpDeferredVars = GraphicsVars::create(mpDeferredPassProgram->getReflector()); - mpLightingVars = GraphicsVars::create(mpLightingPass->getProgram()->getReflector()); - - // Load default model - loadModelFromFile(skDefaultModel, pSample->getCurrentFbo().get()); -} - -void SimpleDeferred::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - GraphicsState* pState = pRenderContext->getGraphicsState().get(); - - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - - // G-Buffer pass - if(mpModel) - { - pRenderContext->clearFbo(mpGBufferFbo.get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::Color | FboAttachmentType::Depth); - pState->setFbo(mpGBufferFbo); - - mpCamera->setDepthRange(mNearZ, mFarZ); - CameraController& ActiveController = getActiveCameraController(); - ActiveController.update(); - - // Animate - if(mAnimate) - { - PROFILE("animate"); - mpModel->animate(pSample->getCurrentTime()); - } - - // Set render state - pState->setRasterizerState(mpCullRastState[mCullMode]); - pState->setDepthStencilState(mpDepthTestDS); - - // Render model - mpModel->bindSamplerToMaterials(mpLinearSampler); - pRenderContext->setGraphicsVars(mpDeferredVars); - pState->setProgram(mpDeferredPassProgram); - ModelRenderer::render(pRenderContext, mpModel, mpCamera.get()); - } - - // Lighting pass (fullscreen quad) - { - pState->setFbo(pTargetFbo); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::Color); - - // Reset render state - pState->setRasterizerState(mpCullRastState[0]); - pState->setBlendState(mpOpaqueBS); - pState->setDepthStencilState(mpNoDepthDS); - - // Set lighting params - ConstantBuffer::SharedPtr pLightCB = mpLightingVars["PerImageCB"]; - pLightCB["gAmbient"] = mAmbientIntensity; - mpDirLight->setIntoProgramVars(mpLightingVars.get(), pLightCB.get(), "gDirLight"); - mpPointLight->setIntoProgramVars(mpLightingVars.get(), pLightCB.get(), "gPointLight"); - // Debug mode - pLightCB->setVariable("gDebugMode", (uint32_t)mDebugMode); - - // Set GBuffer as input - mpLightingVars->setTexture("gGBuf0", mpGBufferFbo->getColorTexture(0)); - mpLightingVars->setTexture("gGBuf1", mpGBufferFbo->getColorTexture(1)); - mpLightingVars->setTexture("gGBuf2", mpGBufferFbo->getColorTexture(2)); - - - // Kick it off - pRenderContext->setGraphicsVars(mpLightingVars); - mpLightingPass->execute(pRenderContext); - } -} - -void SimpleDeferred::onShutdown(SampleCallbacks* pSample) -{ - mpModel.reset(); -} - -bool SimpleDeferred::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - bool bHandled = getActiveCameraController().onKeyEvent(keyEvent); - if(bHandled == false) - { - if(keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - switch(keyEvent.key) - { - case KeyboardEvent::Key::R: - resetCamera(); - break; - default: - bHandled = false; - } - } - } - return bHandled; -} - -bool SimpleDeferred::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return getActiveCameraController().onMouseEvent(mouseEvent); -} - -void SimpleDeferred::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - mpCamera->setFocalLength(21.0f); - mAspectRatio = (float(width) / float(height)); - mpCamera->setAspectRatio(mAspectRatio); - // create G-Buffer - const glm::vec4 clearColor(0.f, 0.f, 0.f, 0.f); - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, Falcor::ResourceFormat::RGBA16Float).setColorTarget(1, Falcor::ResourceFormat::RGBA16Float).setColorTarget(2, Falcor::ResourceFormat::RGBA16Float).setDepthStencilTarget(Falcor::ResourceFormat::D32Float); - mpGBufferFbo = FboHelper::create2D(width, height, fboDesc); -} - -void SimpleDeferred::resetCamera() -{ - if(mpModel) - { - // update the camera position - float radius = mpModel->getRadius(); - const glm::vec3& modelCenter = mpModel->getCenter(); - glm::vec3 camPos = modelCenter; - camPos.z += radius * 4; - - mpCamera->setPosition(camPos); - mpCamera->setTarget(modelCenter); - mpCamera->setUpVector(glm::vec3(0, 1, 0)); - - // Update the controllers - mModelViewCameraController.setModelParams(modelCenter, radius, 4); - mFirstPersonCameraController.setCameraSpeed(radius*0.25f); - mNearZ = std::max(0.1f, mpModel->getRadius() / 750.0f); - mFarZ = radius * 10; - } -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - SimpleDeferred::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.width = 1280; - config.windowDesc.height = 720; - config.windowDesc.resizableWindow = true; - config.windowDesc.title = "Simple Deferred"; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Core/SimpleDeferred/SimpleDeferred.h b/Samples/Core/SimpleDeferred/SimpleDeferred.h deleted file mode 100644 index b2ad558cd..000000000 --- a/Samples/Core/SimpleDeferred/SimpleDeferred.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class SimpleDeferred : public Renderer -{ -public: - ~SimpleDeferred(); - - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onShutdown(SampleCallbacks* pSample) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; -private: - void loadModel(Fbo* pTargetFbo); - void loadModelFromFile(const std::string& filename, Fbo* pTargetFbo); - void resetCamera(); - void renderModelUiElements(Gui* pGui); - - Model::SharedPtr mpModel = nullptr; - ModelViewCameraController mModelViewCameraController; - FirstPersonCameraController mFirstPersonCameraController; - Sampler::SharedPtr mpLinearSampler; - - GraphicsProgram::SharedPtr mpDeferredPassProgram; - GraphicsVars::SharedPtr mpDeferredVars; - - GraphicsVars::SharedPtr mpLightingVars; - FullScreenPass::UniquePtr mpLightingPass; - - float mAspectRatio = 0; - - enum - { - ModelViewCamera, - FirstPersonCamera - } mCameraType = ModelViewCamera; - - CameraController& getActiveCameraController(); - - Camera::SharedPtr mpCamera; - - bool mAnimate = false; - bool mGenerateTangentSpace = true; - glm::vec3 mAmbientIntensity = glm::vec3(0.1f, 0.1f, 0.1f); - - uint32_t mActiveAnimationID = sBindPoseAnimationID; - static const uint32_t sBindPoseAnimationID = (uint32_t)-1; - - RasterizerState::SharedPtr mpCullRastState[3]; // 0 = no culling, 1 = backface culling, 2 = frontface culling - uint32_t mCullMode = 1; - - enum : uint32_t - { - Disabled = 0, - ShowPositions, - ShowNormals, - ShowAlbedo, - ShowLighting - } mDebugMode = Disabled; - - DepthStencilState::SharedPtr mpNoDepthDS; - DepthStencilState::SharedPtr mpDepthTestDS; - BlendState::SharedPtr mpOpaqueBS; - - // G-Buffer - Fbo::SharedPtr mpGBufferFbo; - - DirectionalLight::SharedPtr mpDirLight; - PointLight::SharedPtr mpPointLight; - - float mNearZ = 1e-2f; - float mFarZ = 1e3f; - - static const std::string skDefaultModel; - - std::vector mChangeModeFrames; - std::vector::iterator mChangeModeIt; -}; diff --git a/Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj.filters b/Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj.filters deleted file mode 100644 index f5119c409..000000000 --- a/Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {8f4b49db-cf13-4168-abde-51760776cfd1} - - - - - Data - - - Data - - - - - - - - - \ No newline at end of file diff --git a/Samples/Core/StereoRendering/Data/StereoRendering.ps.hlsl b/Samples/Core/StereoRendering/Data/StereoRendering.ps.hlsl deleted file mode 100644 index 6374a9c56..000000000 --- a/Samples/Core/StereoRendering/Data/StereoRendering.ps.hlsl +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -__import ShaderCommon; -__import Shading; -__import DefaultVS; - -float4 main(VertexOut vOut) : SV_TARGET -{ - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); - - float4 finalColor = float4(0, 0, 0, 1); - - for (uint l = 0; l < gLightsCount; l++) - { - finalColor.rgb += evalMaterial(sd, gLights[l], 1).color.rgb; - } - - return finalColor; -} diff --git a/Samples/Core/StereoRendering/StereoRendering.cpp b/Samples/Core/StereoRendering/StereoRendering.cpp deleted file mode 100644 index b1bfdb289..000000000 --- a/Samples/Core/StereoRendering/StereoRendering.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "StereoRendering.h" - -const std::string StereoRendering::skDefaultScene = "Arcade/Arcade.fscene"; - -static const glm::vec4 kClearColor(0.38f, 0.52f, 0.10f, 1); - - -void StereoRendering::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (pGui->addButton("Load Scene")) - { - loadScene(); - } - - if(VRSystem::instance()) - { - pGui->addCheckBox("Display VR FBO", mShowStereoViews); - } - - if (pGui->addDropdown("Submission Mode", mSubmitModeList, (uint32_t&)mRenderMode)) - { - setRenderMode(); - } -} - -bool displaySpsWarning() -{ -#ifdef FALCOR_D3D12 - logWarning("The sample will run faster if you use NVIDIA's Single Pass Stereo\nIf you have an NVIDIA GPU, download the NVAPI SDK and enable NVAPI support in FalcorConfig.h.\nCheck the readme for instructions"); -#endif - return false; -} - -void StereoRendering::initVR(Fbo* pTargetFbo) -{ - mSubmitModeList.clear(); - mSubmitModeList.push_back({ (int)RenderMode::Mono, "Render to Screen" }); - - if (VRSystem::instance()) - { - // Create the FBOs - Fbo::Desc vrFboDesc; - - vrFboDesc.setColorTarget(0, pTargetFbo->getColorTexture(0)->getFormat()); - vrFboDesc.setDepthStencilTarget(pTargetFbo->getDepthStencilTexture()->getFormat()); - - mpVrFbo = VrFbo::create(vrFboDesc); - - mSubmitModeList.push_back({ (int)RenderMode::Stereo, "Stereo" }); - - if (mSPSSupported) - { - mSubmitModeList.push_back({ (int)RenderMode::SinglePassStereo, "Single Pass Stereo" }); - } - else - { - displaySpsWarning(); - } - } - else - { - msgBox("Can't initialize the VR system. Make sure that your HMD is connected properly"); - } -} - -void StereoRendering::submitStereo(RenderContext* pContext, Fbo::SharedPtr pTargetFbo, bool singlePassStereo) -{ - PROFILE("submitStereo"); - VRSystem::instance()->refresh(); - - // Clear the FBO - pContext->clearFbo(mpVrFbo->getFbo().get(), kClearColor, 1.0f, 0, FboAttachmentType::All); - - // update state - if (singlePassStereo) - { - mpGraphicsState->setProgram(mpMonoSPSProgram); - pContext->setGraphicsVars(mpMonoSPSVars); - } - else - { - mpGraphicsState->setProgram(mpStereoProgram); - pContext->setGraphicsVars(mpStereoVars); - } - mpGraphicsState->setFbo(mpVrFbo->getFbo()); - pContext->pushGraphicsState(mpGraphicsState); - - // Render - mpSceneRenderer->renderScene(pContext); - - // Restore the state - pContext->popGraphicsState(); - - // Submit the views and display them - mpVrFbo->submitToHmd(pContext); - blitTexture(pContext, pTargetFbo.get(), mpVrFbo->getEyeResourceView(VRDisplay::Eye::Left), 0); - blitTexture(pContext, pTargetFbo.get(), mpVrFbo->getEyeResourceView(VRDisplay::Eye::Right), pTargetFbo->getWidth() / 2); -} - -void StereoRendering::submitToScreen(RenderContext* pContext, Fbo::SharedPtr pTargetFbo) -{ - mpGraphicsState->setProgram(mpMonoSPSProgram); - mpGraphicsState->setFbo(pTargetFbo); - pContext->setGraphicsState(mpGraphicsState); - pContext->setGraphicsVars(mpMonoSPSVars); - mpSceneRenderer->renderScene(pContext); -} - -void StereoRendering::setRenderMode() -{ - if(mpScene) - { - mpMonoSPSProgram->removeDefine("_SINGLE_PASS_STEREO"); - - mpGraphicsState->toggleSinglePassStereo(false); - switch(mRenderMode) - { - case RenderMode::SinglePassStereo: - mpMonoSPSProgram->addDefine("_SINGLE_PASS_STEREO"); - mpGraphicsState->toggleSinglePassStereo(true); - mpSceneRenderer->setCameraControllerType(SceneRenderer::CameraControllerType::Hmd); - break; - case RenderMode::Stereo: - mpSceneRenderer->setCameraControllerType(SceneRenderer::CameraControllerType::Hmd); - break; - case RenderMode::Mono: - mpSceneRenderer->setCameraControllerType(SceneRenderer::CameraControllerType::SixDof); - break; - } - } -} - -void StereoRendering::loadScene() -{ - std::string filename; - if(openFileDialog(Scene::kFileExtensionFilters, filename)) - { - loadScene(filename); - } -} - -void StereoRendering::loadScene(const std::string& filename) -{ - mpScene = Scene::loadFromFile(filename); - mpSceneRenderer = SceneRenderer::create(mpScene); - mpMonoSPSProgram = GraphicsProgram::createFromFile("StereoRendering.ps.hlsl", "", "main"); - GraphicsProgram::Desc progDesc; - progDesc.addShaderLibrary("StereoRendering.vs.hlsl").vsEntry("main").addShaderLibrary("StereoRendering.ps.hlsl").psEntry("main").addShaderLibrary("StereoRendering.gs.hlsl").gsEntry("main"); - mpStereoProgram = GraphicsProgram::create(progDesc); - - setRenderMode(); - mpMonoSPSVars = GraphicsVars::create(mpMonoSPSProgram->getReflector()); - mpStereoVars = GraphicsVars::create(mpStereoProgram->getReflector()); - - for (uint32_t m = 0; m < mpScene->getModelCount(); m++) - { - mpScene->getModel(m)->bindSamplerToMaterials(mpTriLinearSampler); - } -} - -void StereoRendering::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - suppress_deprecation - mSPSSupported = gpDevice->isExtensionSupported("VK_NVX_multiview_per_view_attributes"); - - initVR(pSample->getCurrentFbo().get()); - - mpGraphicsState = GraphicsState::create(); - setRenderMode(); - - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpTriLinearSampler = Sampler::create(samplerDesc); - - loadScene(skDefaultScene); -} - -void StereoRendering::blitTexture(RenderContext* pContext, Fbo* pTargetFbo, Texture::SharedPtr pTexture, uint32_t xStart) -{ - if(mShowStereoViews) - { - uvec4 dstRect; - dstRect.x = xStart; - dstRect.y = 0; - dstRect.z = xStart + (pTargetFbo->getWidth() / 2); - dstRect.w = pTargetFbo->getHeight(); - pContext->blit(pTexture->getSRV(0, 1, 0, 1), pTargetFbo->getRenderTargetView(0), uvec4(-1), dstRect); - } -} - -void StereoRendering::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - static uint32_t frameCount = 0u; - - pRenderContext->clearFbo(pTargetFbo.get(), kClearColor, 1.0f, 0, FboAttachmentType::All); - - if(mpSceneRenderer) - { - mpSceneRenderer->update(pSample->getCurrentTime()); - - switch(mRenderMode) - { - case RenderMode::Mono: - submitToScreen(pRenderContext, pTargetFbo); - break; - case RenderMode::SinglePassStereo: - submitStereo(pRenderContext, pTargetFbo, true); - break; - case RenderMode::Stereo: - submitStereo(pRenderContext, pTargetFbo, false); - break; - default: - should_not_get_here(); - } - } - - std::string message = pSample->getFpsMsg(); - message += "\nFrame counter: " + std::to_string(frameCount); - - pSample->renderText(message, glm::vec2(10, 10)); - - frameCount++; -} - -bool StereoRendering::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - if(keyEvent.key == KeyboardEvent::Key::Space && keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - if (VRSystem::instance()) - { - // Cycle through modes - uint32_t nextMode = (uint32_t)mRenderMode + 1; - mRenderMode = (RenderMode)(nextMode % (mSPSSupported ? 3 : 2)); - setRenderMode(); - return true; - } - } - return mpSceneRenderer ? mpSceneRenderer->onKeyEvent(keyEvent) : false; -} - -bool StereoRendering::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mpSceneRenderer ? mpSceneRenderer->onMouseEvent(mouseEvent) : false; -} - -void StereoRendering::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - initVR(pSample->getCurrentFbo().get()); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - StereoRendering::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Stereo Rendering"; - config.windowDesc.height = 1024; - config.windowDesc.width = 1600; - config.windowDesc.resizableWindow = true; - config.deviceDesc.enableVR = true; -#ifdef FALCOR_VK - config.deviceDesc.enableDebugLayer = false; // OpenVR requires an extension that the debug layer doesn't recognize. It causes the application to crash -#endif - -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Core/StereoRendering/StereoRendering.h b/Samples/Core/StereoRendering/StereoRendering.h deleted file mode 100644 index b970ea1e3..000000000 --- a/Samples/Core/StereoRendering/StereoRendering.h +++ /dev/null @@ -1,80 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class StereoRendering : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - -private: - - Scene::SharedPtr mpScene; - SceneRenderer::SharedPtr mpSceneRenderer; - - GraphicsProgram::SharedPtr mpMonoSPSProgram = nullptr; - GraphicsVars::SharedPtr mpMonoSPSVars = nullptr; - - GraphicsProgram::SharedPtr mpStereoProgram = nullptr; - GraphicsVars::SharedPtr mpStereoVars = nullptr; - - GraphicsState::SharedPtr mpGraphicsState = nullptr; - Sampler::SharedPtr mpTriLinearSampler; - - void loadScene(); - void loadScene(const std::string & filename); - - enum class RenderMode - { - Mono, - Stereo, - SinglePassStereo - }; - - bool mSPSSupported = false; - RenderMode mRenderMode = RenderMode::Mono; - Gui::DropdownList mSubmitModeList; - - void submitToScreen(RenderContext* pContext, Fbo::SharedPtr pTargetFbo); - void initVR(Fbo* pTargetFbo); - void blitTexture(RenderContext* pContext, Fbo* pTargetFbo, Texture::SharedPtr pTexture, uint32_t xStart); - VrFbo::UniquePtr mpVrFbo; - bool mShowStereoViews = true; - void submitStereo(RenderContext* pContext, Fbo::SharedPtr pTargetFbo, bool singlePassStereo); - void setRenderMode(); - - static const std::string skDefaultScene; -}; diff --git a/Samples/Core/StereoRendering/StereoRendering.vcxproj.filters b/Samples/Core/StereoRendering/StereoRendering.vcxproj.filters deleted file mode 100644 index 47b0258b1..000000000 --- a/Samples/Core/StereoRendering/StereoRendering.vcxproj.filters +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - {871b6b71-44ab-43ba-a02e-f3ffb5a43e17} - - - - - Data - - - Data - - - Data - - - \ No newline at end of file diff --git a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.cpp b/Samples/Effects/AmbientOcclusion/AmbientOcclusion.cpp deleted file mode 100644 index 3fe5e1acc..000000000 --- a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "AmbientOcclusion.h" - -const std::string AmbientOcclusion::skDefaultModel = "Arcade/Arcade.fbx"; - -void AmbientOcclusion::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (mpSSAO != nullptr) - { - if(pGui->beginGroup("SSAO")) - { - mpSSAO->renderUI(pGui); - pGui->endGroup(); - } - } -} - -void AmbientOcclusion::resetCamera() -{ - if (mpModel) - { - // update the camera position - float radius = mpModel->getRadius(); - const glm::vec3& modelCenter = mpModel->getCenter(); - glm::vec3 camPos = modelCenter; - camPos.z += radius * 4; - - mpCamera->setPosition(camPos); - mpCamera->setTarget(modelCenter); - mpCamera->setUpVector(glm::vec3(0, 1, 0)); - - // Update the controllers - mCameraController.setModelParams(modelCenter, radius, 4); - mNearZ = std::max(0.1f, mpModel->getRadius() / 750.0f); - mFarZ = radius * 10; - } -} - -void AmbientOcclusion::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - // - // "GBuffer" rendering - // - - mpPrePassProgram = GraphicsProgram::createFromFile("AOPrePass.ps.hlsl", "", "main"); - mpPrePassState = GraphicsState::create(); - mpPrePassVars = GraphicsVars::create(mpPrePassProgram->getReflector()); - - RasterizerState::Desc rsDesc; - rsDesc.setCullMode(RasterizerState::CullMode::Back); - mpPrePassState->setRasterizerState(RasterizerState::create(rsDesc)); - - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(true); - mpPrePassState->setDepthStencilState(DepthStencilState::create(dsDesc)); - - mpPrePassState->setProgram(mpPrePassProgram); - - // - // Apply AO pass - // - - Sampler::Desc pointDesc; - pointDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - mpPointSampler = Sampler::create(pointDesc); - - Sampler::Desc linearDesc; - linearDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpLinearSampler = Sampler::create(linearDesc); - - mpCopyPass = FullScreenPass::create("ApplyAO.ps.hlsl"); - mpCopyVars = GraphicsVars::create(mpCopyPass->getProgram()->getReflector()); - - // Effects - mpSSAO = SSAO::create(uvec2(1024)); - - // Model - mpModel = Model::createFromFile(skDefaultModel.c_str()); - - mpCamera = Camera::create(); - mpCamera->setAspectRatio((float)pSample->getCurrentFbo()->getWidth() / (float)pSample->getCurrentFbo()->getHeight()); - mCameraController.attachCamera(mpCamera); - - resetCamera(); -} - -void AmbientOcclusion::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - mpCamera->setDepthRange(mNearZ, mFarZ); - mCameraController.update(); - - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - pRenderContext->clearFbo(mpGBufferFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - // Render Scene - mpPrePassState->setFbo(mpGBufferFbo); - - pRenderContext->setGraphicsState(mpPrePassState); - pRenderContext->setGraphicsVars(mpPrePassVars); - ModelRenderer::render(pRenderContext, mpModel, mpCamera.get()); - - // Generate AO Map - Texture::SharedPtr pAOMap = mpSSAO->generateAOMap(pRenderContext, mpCamera.get(), mpGBufferFbo->getDepthStencilTexture(), mpGBufferFbo->getColorTexture(1)); - - // Apply AO Map to scene - mpCopyVars->setSampler("gSampler", mpLinearSampler); - mpCopyVars->setTexture("gColor", mpGBufferFbo->getColorTexture(0)); - mpCopyVars->setTexture("gAOMap", pAOMap); - pRenderContext->setGraphicsVars(mpCopyVars); - pRenderContext->getGraphicsState()->setFbo(pTargetFbo); - mpCopyPass->execute(pRenderContext); -} - -bool AmbientOcclusion::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - return false; -} - -bool AmbientOcclusion::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mCameraController.onMouseEvent(mouseEvent); -} - -void AmbientOcclusion::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - mpCamera->setFocalLength(21.0f); - mpCamera->setAspectRatio((float)width / (float)height); - - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, Falcor::ResourceFormat::RGBA8Unorm).setColorTarget(1, Falcor::ResourceFormat::RGBA8Unorm).setDepthStencilTarget(Falcor::ResourceFormat::D32Float); - mpGBufferFbo = FboHelper::create2D(width, height, fboDesc); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - AmbientOcclusion::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Ambient Occlusion"; - config.windowDesc.resizableWindow = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.h b/Samples/Effects/AmbientOcclusion/AmbientOcclusion.h deleted file mode 100644 index 226e5d46c..000000000 --- a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.h +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class AmbientOcclusion : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - -private: - - void resetCamera(); - - Model::SharedPtr mpModel; - - Camera::SharedPtr mpCamera; - ModelViewCameraController mCameraController; - - float mNearZ = 1e-2f; - float mFarZ = 1e3f; - - Fbo::SharedPtr mpGBufferFbo; - - GraphicsProgram::SharedPtr mpPrePassProgram; - GraphicsVars::SharedPtr mpPrePassVars; - GraphicsState::SharedPtr mpPrePassState; - - FullScreenPass::UniquePtr mpCopyPass; - GraphicsVars::SharedPtr mpCopyVars; - - Sampler::SharedPtr mpPointSampler; - Sampler::SharedPtr mpLinearSampler; - - SSAO::SharedPtr mpSSAO; - - static const std::string skDefaultModel; -}; diff --git a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj b/Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj deleted file mode 100644 index 5a2267149..000000000 --- a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - Document - - - - - Document - - - - {E6F10A52-9C29-47B0-8BAE-46C146EB7163} - Win32Proj - AmbientOcclusion - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj.filters b/Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj.filters deleted file mode 100644 index f47063572..000000000 --- a/Samples/Effects/AmbientOcclusion/AmbientOcclusion.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - {8d83802f-4f5a-4ff4-a74d-221c75b6ee6b} - - - - - Data - - - Data - - - \ No newline at end of file diff --git a/Samples/Effects/HDRToneMapping/HDRToneMapping.cpp b/Samples/Effects/HDRToneMapping/HDRToneMapping.cpp deleted file mode 100644 index 059209bd9..000000000 --- a/Samples/Effects/HDRToneMapping/HDRToneMapping.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "HDRToneMapping.h" - -using namespace Falcor; - -const Gui::DropdownList HDRToneMapping::kImageList = { { HdrImage::EveningSun, "Evening Sun" }, - { HdrImage::AtTheWindow, "Window" }, - { HdrImage::OvercastDay, "Overcast Day" } }; - -void HDRToneMapping::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - //Create model and camera - mpTeapot = Model::createFromFile("teapot.obj"); - mpCamera = Camera::create(); - float nearZ = 0.1f; - float farZ = mpTeapot->getRadius() * 1000; - mpCamera->setDepthRange(nearZ, farZ); - - //Setup controller - mCameraController.attachCamera(mpCamera); - mCameraController.setModelParams(mpTeapot->getCenter(), mpTeapot->getRadius(), 2.0f); - - //Program - mpMainProg = GraphicsProgram::createFromFile("HDRToneMapping.hlsl", "vs", "ps"); - mpProgramVars = GraphicsVars::create(mpMainProg->getReflector()); - mpGraphicsState = GraphicsState::create(); - mpGraphicsState->setFbo(pSample->getCurrentFbo()); - - //Sampler - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpTriLinearSampler = Sampler::create(samplerDesc); - mpProgramVars->setSampler("gSampler", mpTriLinearSampler); - - mpToneMapper = ToneMapping::create(ToneMapping::Operator::HableUc2); - mpToneMapper->setExposureKey(0.104f); - mLightIntensity = 2.5f; - - loadImage(); -} - -void HDRToneMapping::loadImage() -{ - std::string filename; - switch(mHdrImageIndex) - { - case HdrImage::AtTheWindow: - filename = "LightProbes/20060807_wells6_hd.hdr"; - break; - case HdrImage::EveningSun: - filename = "LightProbes/hallstatt4_hd.hdr"; - break; - case HdrImage::OvercastDay: - filename = "LightProbes/20050806-03_hd.hdr"; - break; - } - - mHdrImage = createTextureFromFile(filename, false, false, Resource::BindFlags::ShaderResource); - mpSkyBox = SkyBox::create(mHdrImage, mpTriLinearSampler); -} - -void HDRToneMapping::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - uint32_t uHdrIndex = static_cast(mHdrImageIndex); - if (pGui->addDropdown("HdrImage", kImageList, uHdrIndex)) - { - mHdrImageIndex = static_cast(uHdrIndex); - loadImage(); - } - pGui->addFloatVar("Surface Roughness", mSurfaceRoughness, 0.01f, 1000, 0.01f); - pGui->addFloatVar("Light Intensity", mLightIntensity, 0.5f, FLT_MAX, 0.1f); - mpToneMapper->renderUI(pGui, "HDR"); -} - -void HDRToneMapping::renderTeapot(RenderContext* pContext) -{ - //Update vars - glm::mat4 wvp = mpCamera->getProjMatrix() * mpCamera->getViewMatrix(); - ConstantBuffer::SharedPtr pPerFrameCB = mpProgramVars["PerFrameCB"]; - pPerFrameCB["gWorldMat"] = glm::mat4(); - pPerFrameCB["gWvpMat"] = wvp; - pPerFrameCB["gEyePosW"] = mpCamera->getPosition(); - pPerFrameCB["gLightIntensity"] = mLightIntensity; - pPerFrameCB["gSurfaceRoughness"] = mSurfaceRoughness; - mpProgramVars->setTexture("gEnvMap", mHdrImage); - - //Set Gfx state - mpGraphicsState->setVao(mpTeapot->getMesh(0)->getVao()); - mpGraphicsState->setProgram(mpMainProg); - pContext->setGraphicsVars(mpProgramVars); - pContext->drawIndexed(mpTeapot->getMesh(0)->getIndexCount(), 0, 0); -} - -void HDRToneMapping::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(mpHdrFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - mCameraController.update(); - - //Render teapot to hdr fbo - mpGraphicsState->pushFbo(mpHdrFbo); - pRenderContext->setGraphicsState(mpGraphicsState); - renderTeapot(pRenderContext); - mpSkyBox->render(pRenderContext, mpCamera.get()); - mpGraphicsState->popFbo(); - - //Run tone mapping - mpToneMapper->execute(pRenderContext, mpHdrFbo->getColorTexture(0), pTargetFbo); - - std::string txt = pSample->getFpsMsg() + '\n'; - pSample->renderText(txt, glm::vec2(10, 10)); -} - -void HDRToneMapping::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - //Camera aspect - mpCamera->setFocalLength(21.0f); - float aspectRatio = (float(width )/ float(height)); - mpCamera->setAspectRatio(aspectRatio); - - //recreate hdr fbo - ResourceFormat format = ResourceFormat::RGBA32Float; - Fbo::Desc desc; - desc.setDepthStencilTarget(ResourceFormat::D16Unorm); - desc.setColorTarget(0u, format); - mpHdrFbo = FboHelper::create2D(width, height, desc); -} - -bool HDRToneMapping::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - return mCameraController.onKeyEvent(keyEvent); -} - -bool HDRToneMapping::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mCameraController.onMouseEvent(mouseEvent); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - HDRToneMapping::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Post Processing"; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Effects/HDRToneMapping/HDRToneMapping.filters b/Samples/Effects/HDRToneMapping/HDRToneMapping.filters deleted file mode 100644 index 24e4d69a9..000000000 --- a/Samples/Effects/HDRToneMapping/HDRToneMapping.filters +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Samples/Effects/HDRToneMapping/HDRToneMapping.h b/Samples/Effects/HDRToneMapping/HDRToneMapping.h deleted file mode 100644 index 8b16ca1d6..000000000 --- a/Samples/Effects/HDRToneMapping/HDRToneMapping.h +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" -using namespace Falcor; - -class HDRToneMapping : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - -private: - Model::SharedPtr mpTeapot; - Texture::SharedPtr mHdrImage; - ModelViewCameraController mCameraController; - Camera::SharedPtr mpCamera; - float mLightIntensity = 1.0f; - float mSurfaceRoughness = 5.0f; - - void renderTeapot(RenderContext* pContext); - - Sampler::SharedPtr mpTriLinearSampler; - GraphicsProgram::SharedPtr mpMainProg = nullptr; - GraphicsVars::SharedPtr mpProgramVars = nullptr; - GraphicsState::SharedPtr mpGraphicsState = nullptr; - - SkyBox::SharedPtr mpSkyBox; - - enum HdrImage - { - EveningSun, - OvercastDay, - AtTheWindow - }; - static const Gui::DropdownList kImageList; - - HdrImage mHdrImageIndex = HdrImage::EveningSun; - Fbo::SharedPtr mpHdrFbo; - ToneMapping::SharedPtr mpToneMapper; - SceneRenderer::SharedPtr mpSceneRenderer; - - void loadImage(); - - std::vector mChangeModeFrames; - std::vector::iterator mChangeModeIt; - uint32_t mToneMapOperatorIndex; -}; diff --git a/Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj.filters b/Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj.filters deleted file mode 100644 index 5509d19a5..000000000 --- a/Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj.filters +++ /dev/null @@ -1,19 +0,0 @@ - - - - - {2f55c824-8939-4b78-a0f4-4773d89165a2} - - - - - Data - - - - - - - - - \ No newline at end of file diff --git a/Samples/Effects/HashedAlpha/HashedAlpha.cpp b/Samples/Effects/HashedAlpha/HashedAlpha.cpp deleted file mode 100644 index 6bd26e046..000000000 --- a/Samples/Effects/HashedAlpha/HashedAlpha.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "HashedAlpha.h" - -const std::string HashedAlpha::skDefaultModel = "alphatest/alpha_test.obj"; - -const Gui::DropdownList HashedAlpha::kModeList = { - { (uint32_t)AlphaTestMode::HashedAlphaIsotropic, "Hashed Alpha Isotropic" }, - { (uint32_t)AlphaTestMode::HashedAlphaAnisotropic, "Hashed Alpha Anisotropic" }, - { (uint32_t)AlphaTestMode::AlphaTest, "Alpha Test" } -}; - -void HashedAlpha::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (pGui->addButton("Load Model")) - { - loadModel(); - } - - pGui->addSeparator(); - - uint32_t mode = (uint32_t)mAlphaTestMode; - if (pGui->addDropdown("Mode", kModeList, mode)) - { - mAlphaTestMode = (AlphaTestMode)mode; - mDirty = true; - } - - pGui->addFloatVar("Hash Scale", mHashScale, 0.01f, 10.0f, 0.01f); - if (pGui->addButton("Apply Scale")) - { - mDirty = true; - } -} - -void HashedAlpha::loadModel(std::string filename) -{ - mpModel = Model::createFromFile(filename.c_str()); - - if (mpModel == nullptr) - { - msgBox("Could not load model"); - return; - } - - // update the camera position - float radius = mpModel->getRadius(); - const glm::vec3& modelCenter = mpModel->getCenter(); - glm::vec3 camPos = modelCenter; - camPos.z += radius * 5.0f; - - mpCamera->setPosition(camPos); - mpCamera->setTarget(modelCenter); - mpCamera->setUpVector(glm::vec3(0, 1, 0)); - mpCamera->setDepthRange(std::max(0.01f, radius / 750.0f), radius * 50.0f); - - mCameraController.setModelParams(modelCenter, radius, 3.5f); -} - -void HashedAlpha::loadModel() -{ - std::string filename; - if (openFileDialog(Model::kFileExtensionFilters, filename)) - { - loadModel(filename); - } -} - -void HashedAlpha::updateProgram() -{ - if (mDirty) - { - mpProgram->setDefines({}); - - switch (mAlphaTestMode) - { - case AlphaTestMode::AlphaTest: - mpProgram->addDefine("_DEFAULT_ALPHA_TEST"); - break; - case AlphaTestMode::HashedAlphaAnisotropic: - mpProgram->addDefine("_HASHED_ALPHA_TEST_ANISOTROPIC"); - break; - } - - mpProgram->addDefine("_HASHED_ALPHA_SCALE", std::to_string(mHashScale)); - mDirty = false; - } -} - -void HashedAlpha::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpProgram = GraphicsProgram::createFromFile("HashedAlpha.ps.hlsl", "", "main"); - mpVars = GraphicsVars::create(mpProgram->getReflector()); - updateProgram(); - - mpState = GraphicsState::create(); - mpState->setProgram(mpProgram); - - mpCamera = Camera::create(); - mpCamera->setAspectRatio((float)pSample->getCurrentFbo()->getWidth() / (float)pSample->getCurrentFbo()->getHeight()); - mCameraController.attachCamera(mpCamera); - - loadModel(skDefaultModel.c_str()); -} - -void HashedAlpha::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.30f, 0.52f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - mpState->setFbo(pTargetFbo); - mCameraController.update(); - - if (mpModel) - { - updateProgram(); - pRenderContext->setGraphicsState(mpState); - pRenderContext->setGraphicsVars(mpVars); - ModelRenderer::render(pRenderContext, mpModel, mpCamera.get()); - } -} - -bool HashedAlpha::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - return mCameraController.onKeyEvent(keyEvent); -} - -bool HashedAlpha::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mCameraController.onMouseEvent(mouseEvent); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - HashedAlpha::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Hashed Alpha Test"; - config.windowDesc.resizableWindow = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Effects/HashedAlpha/HashedAlpha.vcxproj b/Samples/Effects/HashedAlpha/HashedAlpha.vcxproj deleted file mode 100644 index e689f78ff..000000000 --- a/Samples/Effects/HashedAlpha/HashedAlpha.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - true - true - - - - {1EAB59EA-E69B-4ADD-B98B-3E3E8FC1D988} - Win32Proj - HashedAlpha - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Effects/HashedAlpha/HashedAlpha.vcxproj.filters b/Samples/Effects/HashedAlpha/HashedAlpha.vcxproj.filters deleted file mode 100644 index ff5e7108c..000000000 --- a/Samples/Effects/HashedAlpha/HashedAlpha.vcxproj.filters +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - {71acd929-c7e8-4110-9de4-c889c51fbb87} - - - - - Data - - - \ No newline at end of file diff --git a/Samples/Effects/Particles/Particles.cpp b/Samples/Effects/Particles/Particles.cpp deleted file mode 100644 index 315399df8..000000000 --- a/Samples/Effects/Particles/Particles.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Particles.h" -#include - -const Gui::DropdownList kPixelShaders -{ - {0, "ConstColor"}, - {1, "ColorInterp"}, - {2, "Textured"} -}; - -const char* kConstColorPs = "Effects/ParticleConstColor.ps.slang"; -const char* kColorInterpPs = "Effects/ParticleInterpColor.ps.slang"; -const char* kTexturedPs = "Effects/ParticleTexture.ps.slang"; -const std::string kDefaultTexture = "smoke-puff.png"; - -void Particles::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - createSystemGui(pSample->getRenderContext(), pGui); - pGui->addSeparator(); - editPropertiesGui(pGui); -} - -void Particles::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpCamera = Camera::create(); - mpCamera->setPosition(mpCamera->getPosition() + glm::vec3(0, 5, 10)); - mpCamController.attachCamera(mpCamera); - mpTextures.push_back(createTextureFromFile(kDefaultTexture, true, false)); - mGuiData.mTexDropdown.push_back({ 0, kDefaultTexture }); - BlendState::Desc blendDesc; - blendDesc.setRtBlend(0, true); - blendDesc.setRtParams(0, BlendState::BlendOp::Add, BlendState::BlendOp::Add, BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, - BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha); - BlendState::SharedPtr pBlend = BlendState::create(blendDesc); - pRenderContext->getGraphicsState()->setBlendState(pBlend); -} - -void Particles::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - mpCamController.update(); - - for (auto it = mpParticleSystems.begin(); it != mpParticleSystems.end(); ++it) - { - (*it)->update(pRenderContext, pSample->getLastFrameTime(), mpCamera->getViewMatrix()); - (*it)->render(pRenderContext, mpCamera->getViewMatrix(), mpCamera->getProjMatrix()); - } -} - -bool Particles::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - mpCamController.onKeyEvent(keyEvent); - return false; -} - -bool Particles::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - mpCamController.onMouseEvent(mouseEvent); - return false; -} - -void Particles::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - mpCamera->setFocalLength(21.0f); - float aspectRatio = ((float)width / (float)height); - mpCamera->setAspectRatio(aspectRatio); -} - -void Particles::createSystemGui(RenderContext* pContext, Gui* pGui) -{ - if (pGui->beginGroup("Create System")) - { - pGui->addIntVar("Max Particles", mGuiData.mMaxParticles, 0); - pGui->addIntVar("Max Emit Per Frame", mGuiData.mMaxEmitPerFrame, 0); - pGui->addCheckBox("Sorted", mGuiData.mSortSystem); - pGui->addDropdown("PixelShader", kPixelShaders, mGuiData.mPixelShaderIndex); - if (pGui->addButton("Create")) - { - switch ((ExamplePixelShaders)mGuiData.mPixelShaderIndex) - { - case ExamplePixelShaders::ConstColor: - { - ParticleSystem::SharedPtr pSys = ParticleSystem::create(pContext, mGuiData.mMaxParticles, - mGuiData.mMaxEmitPerFrame, kConstColorPs, ParticleSystem::kDefaultSimulateShader, mGuiData.mSortSystem); - mpParticleSystems.push_back(pSys); - mPsData.push_back(vec4(0.f, 0.f, 0.f, 1.f)); - mpParticleSystems[mpParticleSystems.size() - 1]->getDrawVars()->getDefaultBlock()->getConstantBuffer("PsPerFrame")->setBlob(&mPsData[mPsData.size() - 1].colorData.color1, 0, sizeof(vec4)); - break; - } - case ExamplePixelShaders::ColorInterp: - { - ParticleSystem::SharedPtr pSys = ParticleSystem::create(pContext, mGuiData.mMaxParticles, - mGuiData.mMaxEmitPerFrame, kColorInterpPs, ParticleSystem::kDefaultSimulateShader, mGuiData.mSortSystem); - mpParticleSystems.push_back(pSys); - ColorInterpPsPerFrame perFrame; - perFrame.color1 = vec4(1.f, 0.f, 0.f, 1.f); - perFrame.colorT1 = pSys->getParticleDuration(); - perFrame.color2 = vec4(0.f, 0.f, 1.f, 1.f); - perFrame.colorT2 = 0.f; - mPsData.push_back(perFrame); - mpParticleSystems[mpParticleSystems.size() - 1]->getDrawVars()->getDefaultBlock()->getConstantBuffer("PsPerFrame")->setBlob(&mPsData[mPsData.size() - 1].colorData, 0, sizeof(ColorInterpPsPerFrame)); - break; - } - case ExamplePixelShaders::Textured: - { - ParticleSystem::SharedPtr pSys = ParticleSystem::create(pContext, mGuiData.mMaxParticles, - mGuiData.mMaxEmitPerFrame, kTexturedPs, ParticleSystem::kDefaultSimulateShader, mGuiData.mSortSystem); - mpParticleSystems.push_back(pSys); - ColorInterpPsPerFrame perFrame; - perFrame.color1 = vec4(1.f, 1.f, 1.f, 1.f); - perFrame.colorT1 = pSys->getParticleDuration(); - perFrame.color2 = vec4(1.f, 1.f, 1.f, 0.1f); - perFrame.colorT2 = 0.f; - mPsData.push_back(PixelShaderData(0, perFrame)); - pSys->getDrawVars()->setTexture("gTex", mpTextures[0]); - mpParticleSystems[mpParticleSystems.size() - 1]->getDrawVars()->getConstantBuffer("PsPerFrame")->setBlob(&mPsData[mPsData.size() - 1].colorData, 0, sizeof(ColorInterpPsPerFrame)); - break; - } - default: - { - should_not_get_here(); - } - } - } - pGui->endGroup(); - } -} - -void Particles::editPropertiesGui(Gui* pGui) -{ - pGui->addIntVar("System index", mGuiData.mSystemIndex, 0, ((int32_t)mpParticleSystems.size()) - 1); - pGui->addSeparator(); - - //If there are no systems yet, don't let user touch properties - if (mGuiData.mSystemIndex < 0) - return; - - //properties shared by all systems - if (pGui->beginGroup("Common Properties")) - { - mpParticleSystems[mGuiData.mSystemIndex]->renderUi(pGui); - pGui->endGroup(); - } - //pixel shader specific properties - if (pGui->beginGroup("Pixel Shader Properties")) - { - switch ((ExamplePixelShaders)mPsData[mGuiData.mSystemIndex].type) - { - case ExamplePixelShaders::ConstColor: - { - if (pGui->addRgbaColor("Color", mPsData[mGuiData.mSystemIndex].colorData.color1)) - { - mpParticleSystems[mGuiData.mSystemIndex]->getDrawVars()->getConstantBuffer("PsPerFrame")->setBlob(&mPsData[mGuiData.mSystemIndex].colorData.color1, 0, sizeof(vec4)); - } - break; - } - case ExamplePixelShaders::ColorInterp: - { - updateColorInterpolation(pGui); - break; - } - case ExamplePixelShaders::Textured: - { - if (pGui->addButton("Add Texture")) - { - std::string filename; - FileDialogFilterVec filters = { {"bmp"}, {"jpg"}, {"dds"}, {"png"}, {"tiff"}, {"tif"}, {"tga"} }; - openFileDialog(filters, filename); - mpTextures.push_back(createTextureFromFile(filename, true, false)); - mGuiData.mTexDropdown.push_back({ (uint32_t)mGuiData.mTexDropdown.size(), filename }); - } - - uint32_t texIndex = mPsData[mGuiData.mSystemIndex].texIndex; - if (pGui->addDropdown("Texture", mGuiData.mTexDropdown, texIndex)) - { - mpParticleSystems[mGuiData.mSystemIndex]->getDrawVars()->setTexture("gTex", mpTextures[texIndex]); - mPsData[mGuiData.mSystemIndex].texIndex = texIndex; - } - - updateColorInterpolation(pGui); - break; - } - default: - should_not_get_here(); - } - - pGui->endGroup(); - } -} - -void Particles::updateColorInterpolation(Gui* pGui) -{ - bool dirty = pGui->addRgbaColor("Color1", mPsData[mGuiData.mSystemIndex].colorData.color1); - dirty |= pGui->addFloatVar("Color T1", mPsData[mGuiData.mSystemIndex].colorData.colorT1); - dirty |= pGui->addRgbaColor("Color2", mPsData[mGuiData.mSystemIndex].colorData.color2); - dirty |= pGui->addFloatVar("Color T2", mPsData[mGuiData.mSystemIndex].colorData.colorT2); - - if (dirty) - { - mpParticleSystems[mGuiData.mSystemIndex]->getDrawVars()->getConstantBuffer("PsPerFrame")->setBlob(&mPsData[mGuiData.mSystemIndex].colorData, 0, sizeof(ColorInterpPsPerFrame)); - } -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ -#ifdef FALCOR_VK - msgBox("Support for Particle Systems in Vulkan is coming soon!"); - return 0 ; -#endif - - Particles::UniquePtr pRenderer = std::make_unique(); - - SampleConfig config; - config.windowDesc.title = "Particles"; - config.windowDesc.resizableWindow = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Effects/Particles/Particles.h b/Samples/Effects/Particles/Particles.h deleted file mode 100644 index e2e243c38..000000000 --- a/Samples/Effects/Particles/Particles.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class Particles : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - -private: - enum class ExamplePixelShaders - { - ConstColor = 0, - ColorInterp = 1, - Textured = 2, - Count - }; - - struct GuiData - { - int32_t mSystemIndex = -1; - uint32_t mPixelShaderIndex = 0; - bool mSortSystem = false; - int32_t mMaxParticles = 4096; - int32_t mMaxEmitPerFrame = 512; - Gui::DropdownList mTexDropdown; - } mGuiData; - - struct PixelShaderData - { - PixelShaderData(vec4 color) { type = ExamplePixelShaders::ConstColor; colorData.color1 = color; } - PixelShaderData(ColorInterpPsPerFrame data) { type = ExamplePixelShaders::ColorInterp; colorData = data; } - PixelShaderData(uint32_t newTexIndex, ColorInterpPsPerFrame data) - { - type = ExamplePixelShaders::Textured; - texIndex = newTexIndex; - colorData = data; - } - - ExamplePixelShaders type; - ColorInterpPsPerFrame colorData; - uint32_t texIndex; - }; - - void createSystemGui(RenderContext* pContext, Gui* pGui); - void editPropertiesGui(Gui* pGui); - void updateColorInterpolation(Gui* pGui); - - std::vector mpParticleSystems; - Camera::SharedPtr mpCamera; - FirstPersonCameraController mpCamController; - std::vector mPsData; - std::vector mpTextures; -}; diff --git a/Samples/Effects/Particles/Particles.vcxproj b/Samples/Effects/Particles/Particles.vcxproj deleted file mode 100644 index fce000d3c..000000000 --- a/Samples/Effects/Particles/Particles.vcxproj +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - {4BD89013-BE22-47CC-BCEC-8A406C8061A0} - Win32Proj - Particles - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Effects/Particles/Particles.vcxproj.filters b/Samples/Effects/Particles/Particles.vcxproj.filters deleted file mode 100644 index f571fac30..000000000 --- a/Samples/Effects/Particles/Particles.vcxproj.filters +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Samples/Effects/Shadows/Shadows.cpp b/Samples/Effects/Shadows/Shadows.cpp deleted file mode 100644 index 244b21ce4..000000000 --- a/Samples/Effects/Shadows/Shadows.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Shadows.h" - -const std::string Shadows::skDefaultScene = "Arcade/Arcade.fscene"; -const Gui::DropdownList Shadows::skDebugModeList = { - { (uint32_t)Shadows::DebugMode::None, "None" }, - { (uint32_t)Shadows::DebugMode::ShadowMap, "Shadow Map" }, - { (uint32_t)Shadows::DebugMode::VisibilityBuffer, "Visibility Buffer" } -}; -const std::string kVisPixelShaderFile = "VisualizeMap.slang"; - -void Shadows::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (pGui->addButton("Load Scene")) - { - displayLoadSceneDialog(); - } - - pGui->addCheckBox("Update Shadow Map", mControls.updateShadowMap); - if(pGui->addIntVar("Cascade Count", mControls.cascadeCount, 1u, CSM_MAX_CASCADES)) - { - for (uint32_t i = 0; i < mpCsmTech.size(); i++) - { - mpCsmTech[i]->setCascadeCount(mControls.cascadeCount); - } - createVisualizationProgram(); - } - - bool visualizeCascades = mPerFrameCBData.visualizeCascades != 0; - pGui->addCheckBox("Visualize Cascades", visualizeCascades); - for (auto it = mpCsmTech.begin(); it != mpCsmTech.end(); ++it) - { - //Tell csm objects whether or not to store cascade info in their visibility buffers - (*it)->toggleCascadeVisualization(visualizeCascades); - } - mPerFrameCBData.visualizeCascades = visualizeCascades; - - pGui->addDropdown("Debug Mode", skDebugModeList, mControls.debugMode); - pGui->addIntVar("Displayed Cascade", mControls.displayedCascade, 0u, mControls.cascadeCount - 1); - if (pGui->addIntVar("LightIndex", mControls.lightIndex, 0u, mpScene->getLightCount() - 1)) - { - mLightingPass.pProgram->addDefine("_LIGHT_INDEX", std::to_string(mControls.lightIndex)); - } - - std::string groupName = "Light " + std::to_string(mControls.lightIndex); - if (pGui->beginGroup(groupName.c_str())) - { - mpScene->getLight(mControls.lightIndex)->renderUI(pGui); - pGui->endGroup(); - } - mpCsmTech[mControls.lightIndex]->renderUI(pGui, "CSM"); -} - -void Shadows::displayLoadSceneDialog() -{ - std::string filename; - if(openFileDialog(Scene::kFileExtensionFilters, filename)) - { - createScene(filename); - } -} - -void Shadows::setLightIndex(int32_t index) -{ - mControls.lightIndex = max(min(index, (int32_t)mpScene->getLightCount() - 1), 0); -} - -void Shadows::createScene(const std::string& filename) -{ - // Load the scene - mpScene = Scene::loadFromFile(filename); - - // Create the renderer - mpRenderer = SceneRenderer::create(mpScene); - mpRenderer->setCameraControllerType(SceneRenderer::CameraControllerType::FirstPerson); - mpRenderer->toggleStaticMaterialCompilation(false); - if(mpScene->getPathCount() && mpScene->getPath(0)) - { - mpScene->getPath(0)->detachObject(mpScene->getActiveCamera()); - } - - auto lightCount = mpScene->getLightCount(); - mpCsmTech.resize(lightCount); - mpVisibilityBuffers.resize(lightCount); - for(uint32_t i = 0; i < lightCount; i++) - { - mpCsmTech[i] = CascadedShadowMaps::create(mpScene->getLight(i), 2048, 2048, mWindowDimensions.x, mWindowDimensions.y, mpScene, mControls.cascadeCount); - mpCsmTech[i]->setFilterMode(CsmFilterHwPcf); - mpCsmTech[i]->setVsmLightBleedReduction(0.3f); - } - setLightIndex(0); - - // Create the main effect - GraphicsProgram::Desc lightingPassProgDesc; - lightingPassProgDesc.addShaderLibrary("Shadows.slang"); - lightingPassProgDesc.vsEntry("vsMain").psEntry("psMain"); - mLightingPass.pProgram = GraphicsProgram::create(lightingPassProgDesc); - mLightingPass.pProgram->addDefine("_LIGHT_COUNT", std::to_string(mpScene->getLightCount())); - mLightingPass.pProgram->addDefine("_LIGHT_INDEX", std::to_string(mControls.lightIndex)); - mLightingPass.pProgramVars = GraphicsVars::create(mLightingPass.pProgram->getReflector()); - ConstantBuffer::SharedPtr pCB = mLightingPass.pProgramVars->getConstantBuffer(0, 0, 0); -} - -void Shadows::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - auto pTargetFbo = pRenderContext->getGraphicsState()->getFbo(); - mWindowDimensions.x = pTargetFbo->getWidth(); - mWindowDimensions.y = pTargetFbo->getHeight(); - createScene(skDefaultScene); - createVisualizationProgram(); -} - -void Shadows::runMainPass(RenderContext* pContext) -{ - //Only part of the gfx state I actually want to set - pContext->getGraphicsState()->setProgram(mLightingPass.pProgram); - - //vars - ConstantBuffer::SharedPtr pPerFrameCB = mLightingPass.pProgramVars->getConstantBuffer(0, 0, 0); - pPerFrameCB->setBlob(&mPerFrameCBData, 0, sizeof(mPerFrameCBData)); - pContext->pushGraphicsVars(mLightingPass.pProgramVars); - - mpRenderer->renderScene(pContext); - - pContext->popGraphicsVars(); -} - -void Shadows::displayShadowMap(RenderContext* pContext) -{ - ParameterBlock::SharedPtr pDefaultBlock = mShadowVisualizer.pShadowMapProgramVars->getDefaultBlock(); - mShadowVisualizer.pShadowMapProgramVars->setTexture("gTexture", mpCsmTech[mControls.lightIndex]->getShadowMap()); - if (mControls.cascadeCount > 1) - { - mShadowVisualizer.pShadowMapProgramVars["PerImageCB"]->setBlob(&mControls.displayedCascade, mOffsets.displayedCascade, sizeof(mControls.displayedCascade)); - } - pContext->pushGraphicsVars(mShadowVisualizer.pShadowMapProgramVars); - mShadowVisualizer.pShadowMapProgram->execute(pContext); - pContext->popGraphicsVars(); -} - -void Shadows::displayVisibilityBuffer(RenderContext* pContext) -{ - mShadowVisualizer.pVisibilityBufferProgramVars->setTexture("gTexture", mpVisibilityBuffers[mControls.lightIndex]); - - pContext->pushGraphicsVars(mShadowVisualizer.pVisibilityBufferProgramVars); - mShadowVisualizer.pVisibilityBufferProgram->execute(pContext); - pContext->popGraphicsVars(); -} - -void Shadows::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - if(mpScene) - { - // Update the scene - mpRenderer->update(pSample->getCurrentTime()); - - // Run the shadow pass - if(mControls.updateShadowMap) - { - mPerFrameCBData.camVpAtLastCsmUpdate = mpScene->getActiveCamera()->getViewProjMatrix(); - for(uint32_t i = 0; i < mpCsmTech.size(); i++) - { - mpVisibilityBuffers[i] = mpCsmTech[i]->generateVisibilityBuffer(pRenderContext, mpScene->getActiveCamera().get(), nullptr); - } - } - - // Put visibility buffers in program vars - for(uint32_t i = 0; i < mpCsmTech.size(); i++) - { - std::string var = "gVisibilityBuffers[" + std::to_string(i) + "]"; - mLightingPass.pProgramVars->setTexture(var, mpVisibilityBuffers[i]); - } - - if(mControls.debugMode == (uint32_t)DebugMode::ShadowMap) - { - displayShadowMap(pRenderContext); - } - else if (mControls.debugMode == (uint32_t)DebugMode::VisibilityBuffer) - { - displayVisibilityBuffer(pRenderContext); - } - else - { - runMainPass(pRenderContext); - } - } - - pSample->renderText(pSample->getFpsMsg(), glm::vec2(10, 10)); -} - -bool Shadows::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - return mpRenderer->onKeyEvent(keyEvent); -} - -bool Shadows::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mpRenderer->onMouseEvent(mouseEvent); -} - -void Shadows::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - //Camera aspect - Camera::SharedPtr activeCamera = mpScene->getActiveCamera(); - activeCamera->setFocalLength(21.0f); - float aspectRatio = (float(width) / float(height)); - activeCamera->setAspectRatio(aspectRatio); - - //Update window width/height for visibility buffer - mWindowDimensions.x = width; - mWindowDimensions.y = height; - for (auto it = mpCsmTech.begin(); it != mpCsmTech.end(); ++it) - { - (*it)->onResize(width, height); - } -} - -void Shadows::createVisualizationProgram() -{ - // Create the shadow visualizer for shadow maps - mShadowVisualizer.pShadowMapProgram = FullScreenPass::create(kVisPixelShaderFile); - if(mControls.cascadeCount > 1) - { - mShadowVisualizer.pShadowMapProgram->getProgram()->addDefine("_USE_2D_ARRAY"); - mShadowVisualizer.pShadowMapProgramVars = GraphicsVars::create(mShadowVisualizer.pShadowMapProgram->getProgram()->getReflector()); - mOffsets.displayedCascade = static_cast(mShadowVisualizer.pShadowMapProgramVars->getConstantBuffer("PerImageCB")->getVariableOffset("cascade")); - } - else - { - mShadowVisualizer.pShadowMapProgramVars = GraphicsVars::create(mShadowVisualizer.pShadowMapProgram->getProgram()->getReflector()); - } - - // Create the shadow visualizer for visibility buffers - mShadowVisualizer.pVisibilityBufferProgram = FullScreenPass::create(kVisPixelShaderFile); - mShadowVisualizer.pVisibilityBufferProgramVars = GraphicsVars::create(mShadowVisualizer.pVisibilityBufferProgram->getProgram()->getReflector()); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - Shadows::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Shadows Sample"; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Effects/Shadows/Shadows.h b/Samples/Effects/Shadows/Shadows.h deleted file mode 100644 index 570bca76e..000000000 --- a/Samples/Effects/Shadows/Shadows.h +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class Shadows : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - -private: - void displayShadowMap(RenderContext* pContext); - void displayVisibilityBuffer(RenderContext* pContext); - void runMainPass(RenderContext* pContext); - void createVisualizationProgram(); - void createScene(const std::string& filename); - void displayLoadSceneDialog(); - void setLightIndex(int32_t index); - - std::vector mpCsmTech; - Scene::SharedPtr mpScene; - - struct - { - FullScreenPass::UniquePtr pShadowMapProgram; - GraphicsVars::SharedPtr pShadowMapProgramVars; - FullScreenPass::UniquePtr pVisibilityBufferProgram; - GraphicsVars::SharedPtr pVisibilityBufferProgramVars; - } mShadowVisualizer; - - struct - { - GraphicsProgram::SharedPtr pProgram; - GraphicsVars::SharedPtr pProgramVars; - } mLightingPass; - - Sampler::SharedPtr mpLinearSampler = nullptr; - - SceneRenderer::SharedPtr mpRenderer; - - enum class DebugMode { None = 0, ShadowMap = 1, VisibilityBuffer = 2, Count = 3 }; - static const Gui::DropdownList skDebugModeList; - struct Controls - { - bool updateShadowMap = true; - uint32_t debugMode = (uint32_t)DebugMode::None; - int32_t displayedCascade = 0; - int32_t cascadeCount = 4; - int32_t lightIndex = 0; - }; - Controls mControls; - - - struct ShadowOffsets - { - uint32_t displayedCascade; - } mOffsets; - - //non csm data in this cb so it can be sent as a single blob - struct PerFrameCBData - { - //This is effectively a bool, but bool only takes up 1 byte which messes up setBlob - glm::mat4 camVpAtLastCsmUpdate = glm::mat4(); - uint32_t visualizeCascades = 0u; - } mPerFrameCBData; - - static const std::string skDefaultScene; - glm::uvec2 mWindowDimensions; - std::vector mpVisibilityBuffers; - - std::vector mFilterFrames; - std::vector::iterator mFilterFramesIt; -}; diff --git a/Samples/Effects/Shadows/Shadows.vcxproj b/Samples/Effects/Shadows/Shadows.vcxproj deleted file mode 100644 index caaabb72b..000000000 --- a/Samples/Effects/Shadows/Shadows.vcxproj +++ /dev/null @@ -1,101 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {613640EA-CBBD-4B9D-931C-00110D5C4007} - Win32Proj - Shadows - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - - - - - - - true - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - - \ No newline at end of file diff --git a/Samples/Effects/Shadows/Shadows.vcxproj.filters b/Samples/Effects/Shadows/Shadows.vcxproj.filters deleted file mode 100644 index f0205e153..000000000 --- a/Samples/Effects/Shadows/Shadows.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - {f09ab360-293e-4517-bce2-da71cb04bdbd} - - - - - Shaders - - - Shaders - - - \ No newline at end of file diff --git a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.cpp b/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.cpp deleted file mode 100644 index e14aa07e8..000000000 --- a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "SkyBoxRenderer.h" - -const std::string SkyBoxRenderer::skDefaultSkyBoxTexture = "Cubemaps/Sorsele3/Sorsele3.dds"; - -void SkyBoxRenderer::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - if (pGui->addButton("Load TexCube")) - { - loadTexture(); - } - float s = mpSkybox->getScale(); - if (pGui->addFloatVar("Cubemap Scale", s, 0.01f, FLT_MAX, 0.01f)) - { - mpSkybox->setScale(s); - } -} - -void SkyBoxRenderer::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpCamera = Camera::create(); - mpCameraController = SixDoFCameraController::SharedPtr(new SixDoFCameraController); - mpCameraController->attachCamera(mpCamera); - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpTriLinearSampler = Sampler::create(samplerDesc); - - mpSkybox = SkyBox::create(skDefaultSkyBoxTexture, true, mpTriLinearSampler); -} - -void SkyBoxRenderer::loadTexture() -{ - std::string filename; - if (openFileDialog({ {"dds"} }, filename)) - { - mpSkybox = SkyBox::create(filename, true, mpTriLinearSampler); - } -} - -void SkyBoxRenderer::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - if(mpSkybox) - { - mpCameraController->update(); - mpSkybox->render(pRenderContext, mpCamera.get()); - } -} - -bool SkyBoxRenderer::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - return mpCameraController->onKeyEvent(keyEvent); -} - -bool SkyBoxRenderer::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mpCameraController->onMouseEvent(mouseEvent); -} - -void SkyBoxRenderer::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - mpCamera->setFocalLength(60.0f); - mpCamera->setAspectRatio((float)width / (float)height); - mpCamera->setDepthRange(0.01f, 1000); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - SkyBoxRenderer::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Skybox Sample"; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.h b/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.h deleted file mode 100644 index 8a8fb3f06..000000000 --- a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.h +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class SkyBoxRenderer : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - -private: - void loadTexture(); - - Camera::SharedPtr mpCamera; - SixDoFCameraController::SharedPtr mpCameraController; - SkyBox::SharedPtr mpSkybox; - Sampler::SharedPtr mpTriLinearSampler; - - static const std::string skDefaultSkyBoxTexture; - - std::vector mChangeViewFrames; - std::vector::iterator mChangeViewIt; -}; diff --git a/Samples/ForwardRenderer/Data/ApplyAO.ps.slang b/Samples/ForwardRenderer/Data/ApplyAO.ps.slang deleted file mode 100644 index fc77fa35e..000000000 --- a/Samples/ForwardRenderer/Data/ApplyAO.ps.slang +++ /dev/null @@ -1,38 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -__import Helpers; - -SamplerState gSampler; - -Texture2D gColor; -Texture2D gAOMap; - -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 -{ - return applyAmbientOcclusion(gColor.SampleLevel(gSampler, texC, 0), gAOMap, gSampler, texC); -} diff --git a/Samples/ForwardRenderer/Data/ForwardRenderer.slang b/Samples/ForwardRenderer/Data/ForwardRenderer.slang deleted file mode 100644 index 34709be4c..000000000 --- a/Samples/ForwardRenderer/Data/ForwardRenderer.slang +++ /dev/null @@ -1,122 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -__import ShaderCommon; -__import DefaultVS; -__import Effects.CascadedShadowMap; -__import Shading; -__import Helpers; -__import BRDF; - -layout(binding = 0) cbuffer PerFrameCB : register(b0) -{ - CsmData gCsmData; - float4x4 camVpAtLastCsmUpdate; - float2 gRenderTargetDim; - float gOpacityScale; -}; - -layout(set = 1, binding = 1) SamplerState gSampler; -Texture2D gVisibilityBuffer; - -struct MainVsOut -{ - VertexOut vsData; - float shadowsDepthC : DEPTH; -}; - -MainVsOut vs(VertexIn vIn) -{ - MainVsOut vsOut; - vsOut.vsData = defaultVS(vIn); - -#ifdef _OUTPUT_MOTION_VECTORS - vsOut.vsData.prevPosH.xy += vsOut.vsData.prevPosH.w * 2 * float2(gCamera.jitterX, gCamera.jitterY); -#endif - - vsOut.shadowsDepthC = mul(float4(vsOut.vsData.posW, 1), camVpAtLastCsmUpdate).z; - return vsOut; -} - -struct PsOut -{ - float4 color : SV_TARGET0; - float4 normal : SV_TARGET1; -#ifdef _OUTPUT_MOTION_VECTORS - float2 motion : SV_TARGET2; -#endif -}; - -PsOut ps(MainVsOut vOut, float4 pixelCrd : SV_POSITION) -{ - PsOut psOut; - - ShadingData sd = prepareShadingData(vOut.vsData, gMaterial, gCamera.posW); - - float4 finalColor = float4(0, 0, 0, 1); - - [unroll] - for (uint l = 0; l < _LIGHT_COUNT; l++) - { - float shadowFactor = 1; -#ifdef _ENABLE_SHADOWS - if (l == 0) - { - shadowFactor = gVisibilityBuffer.Load(int3(vOut.vsData.posH.xy, 0)).r; - shadowFactor *= sd.opacity; - } -#endif - finalColor.rgb += evalMaterial(sd, gLights[l], shadowFactor).color.rgb; - } - - // Add the emissive component - finalColor.rgb += sd.emissive; - -#ifdef _ENABLE_TRANSPARENCY - finalColor.a = sd.opacity * gOpacityScale; -#endif - -#ifdef _ENABLE_REFLECTIONS - finalColor.rgb += evalMaterial(sd, gLightProbe).color.rgb; -#endif - - // Add light-map - finalColor.rgb += sd.diffuse * sd.lightMap.rgb; - - psOut.color = finalColor; - psOut.normal = float4(vOut.vsData.normalW * 0.5f + 0.5f, 1.0f); - -#ifdef _OUTPUT_MOTION_VECTORS - psOut.motion = calcMotionVector(pixelCrd.xy, vOut.vsData.prevPosH, gRenderTargetDim); -#endif - -#if defined(_VISUALIZE_CASCADES) && defined(_ENABLE_SHADOWS) - float3 cascadeColor = gVisibilityBuffer.Load(int3(vOut.vsData.posH.xy, 0)).gba; - psOut.color.rgb *= cascadeColor; -#endif - return psOut; -} diff --git a/Samples/ForwardRenderer/ForwardRenderer.cpp b/Samples/ForwardRenderer/ForwardRenderer.cpp deleted file mode 100644 index 8c3047d44..000000000 --- a/Samples/ForwardRenderer/ForwardRenderer.cpp +++ /dev/null @@ -1,577 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "ForwardRenderer.h" - -const std::string ForwardRenderer::skDefaultScene = "Arcade/Arcade.fscene"; - -void ForwardRenderer::initDepthPass() -{ - mDepthPass.pProgram = GraphicsProgram::createFromFile("DepthPass.ps.slang", "", "main"); - mDepthPass.pVars = GraphicsVars::create(mDepthPass.pProgram->getReflector()); -} - -void ForwardRenderer::initLightingPass() -{ - mLightingPass.pProgram = GraphicsProgram::createFromFile("ForwardRenderer.slang", "vs", "ps"); - mLightingPass.pProgram->addDefine("_LIGHT_COUNT", std::to_string(mpSceneRenderer->getScene()->getLightCount())); - initControls(); - mLightingPass.pVars = GraphicsVars::create(mLightingPass.pProgram->getReflector()); - - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(true).setStencilTest(false)./*setDepthWriteMask(false).*/setDepthFunc(DepthStencilState::Func::LessEqual); - mLightingPass.pDsState = DepthStencilState::create(dsDesc); - - RasterizerState::Desc rsDesc; - rsDesc.setCullMode(RasterizerState::CullMode::None); - mLightingPass.pNoCullRS = RasterizerState::create(rsDesc); - - BlendState::Desc bsDesc; - bsDesc.setRtBlend(0, true).setRtParams(0, BlendState::BlendOp::Add, BlendState::BlendOp::Add, BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, BlendState::BlendFunc::One, BlendState::BlendFunc::Zero); - mLightingPass.pAlphaBlendBS = BlendState::create(bsDesc); -} - -void ForwardRenderer::initShadowPass(uint32_t windowWidth, uint32_t windowHeight) -{ - mShadowPass.pCsm = CascadedShadowMaps::create(mpSceneRenderer->getScene()->getLight(0), 2048, 2048, windowWidth, windowHeight, mpSceneRenderer->getScene()->shared_from_this()); - mShadowPass.pCsm->setFilterMode(CsmFilterEvsm4); - mShadowPass.pCsm->setVsmLightBleedReduction(0.3f); - mShadowPass.pCsm->setVsmMaxAnisotropy(4); - mShadowPass.pCsm->setEvsmBlur(7, 3); -} - -void ForwardRenderer::initSSAO() -{ - mSSAO.pSSAO = SSAO::create(uvec2(1024)); - mSSAO.pApplySSAOPass = FullScreenPass::create("ApplyAO.ps.slang"); - mSSAO.pVars = GraphicsVars::create(mSSAO.pApplySSAOPass->getProgram()->getReflector()); - - Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mSSAO.pVars->setSampler("gSampler", Sampler::create(desc)); -} - -void ForwardRenderer::setSceneSampler(uint32_t maxAniso) -{ - Scene* pScene = mpSceneRenderer->getScene().get(); - Sampler::Desc samplerDesc; - samplerDesc.setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap).setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear).setMaxAnisotropy(maxAniso); - mpSceneSampler = Sampler::create(samplerDesc); - pScene->bindSampler(mpSceneSampler); -} - -void ForwardRenderer::applyCustomSceneVars(const Scene* pScene, const std::string& filename) -{ - std::string folder = getDirectoryFromFile(filename); - - Scene::UserVariable var = pScene->getUserVariable("sky_box"); - if (var.type == Scene::UserVariable::Type::String) initSkyBox(folder + '/' + var.str); - - var = pScene->getUserVariable("opacity_scale"); - if (var.type == Scene::UserVariable::Type::Double) mOpacityScale = (float)var.d64; -} - -void ForwardRenderer::initScene(SampleCallbacks* pSample, Scene::SharedPtr pScene) -{ - if (pScene->getCameraCount() == 0) - { - // Place the camera above the center, looking slightly downwards - const Model* pModel = pScene->getModel(0).get(); - Camera::SharedPtr pCamera = Camera::create(); - - vec3 position = pModel->getCenter(); - float radius = pModel->getRadius(); - position.y += 0.1f * radius; - pScene->setCameraSpeed(radius * 0.03f); - - pCamera->setPosition(position); - pCamera->setTarget(position + vec3(0, -0.3f, -radius)); - pCamera->setDepthRange(0.1f, radius * 10); - - pScene->addCamera(pCamera); - } - - if (pScene->getLightCount() == 0) - { - // Create a directional light - DirectionalLight::SharedPtr pDirLight = DirectionalLight::create(); - pDirLight->setWorldDirection(vec3(-0.189f, -0.861f, -0.471f)); - pDirLight->setIntensity(vec3(1, 1, 0.985f) * 10.0f); - pDirLight->setName("DirLight"); - pScene->addLight(pDirLight); - } - - if (pScene->getLightProbeCount() > 0) - { - const LightProbe::SharedPtr& pProbe = pScene->getLightProbe(0); - pProbe->setRadius(pScene->getRadius()); - pProbe->setPosW(pScene->getCenter()); - pProbe->setSampler(mpSceneSampler); - } - - mpSceneRenderer = ForwardRendererSceneRenderer::create(pScene); - mpSceneRenderer->setCameraControllerType(SceneRenderer::CameraControllerType::FirstPerson); - mpSceneRenderer->toggleStaticMaterialCompilation(mPerMaterialShader); - setSceneSampler(mpSceneSampler ? mpSceneSampler->getMaxAnisotropy() : 4); - setActiveCameraAspectRatio(pSample->getCurrentFbo()->getWidth(), pSample->getCurrentFbo()->getHeight()); - initDepthPass(); - initLightingPass(); - auto pTargetFbo = pSample->getCurrentFbo(); - initShadowPass(pTargetFbo->getWidth(), pTargetFbo->getHeight()); - initSSAO(); - initAA(pSample); - - mControls[EnableReflections].enabled = pScene->getLightProbeCount() > 0; - applyLightingProgramControl(ControlID::EnableReflections); - - pSample->setCurrentTime(0); -} - -void ForwardRenderer::resetScene() -{ - mpSceneRenderer = nullptr; - mSkyBox.pEffect = nullptr; -} - -void ForwardRenderer::loadModel(SampleCallbacks* pSample, const std::string& filename, bool showProgressBar) -{ - Mesh::resetGlobalIdCounter(); - resetScene(); - - ProgressBar::SharedPtr pBar; - if (showProgressBar) - { - pBar = ProgressBar::create("Loading Model"); - } - - Model::SharedPtr pModel = Model::createFromFile(filename.c_str()); - if (!pModel) return; - Scene::SharedPtr pScene = Scene::create(); - pScene->addModelInstance(pModel, "instance"); - - initScene(pSample, pScene); -} - -void ForwardRenderer::loadScene(SampleCallbacks* pSample, const std::string& filename, bool showProgressBar) -{ - Mesh::resetGlobalIdCounter(); - resetScene(); - - ProgressBar::SharedPtr pBar; - if (showProgressBar) - { - pBar = ProgressBar::create("Loading Scene", 100); - } - - Scene::SharedPtr pScene = Scene::loadFromFile(filename); - - if (pScene != nullptr) - { - initScene(pSample, pScene); - applyCustomSceneVars(pScene.get(), filename); - applyCsSkinningMode(); - } -} - -void ForwardRenderer::initSkyBox(const std::string& name) -{ - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mSkyBox.pSampler = Sampler::create(samplerDesc); - mSkyBox.pEffect = SkyBox::create(name, true, mSkyBox.pSampler); - DepthStencilState::Desc dsDesc; - dsDesc.setDepthFunc(DepthStencilState::Func::Always); - mSkyBox.pDS = DepthStencilState::create(dsDesc); -} - -void ForwardRenderer::updateLightProbe(const LightProbe::SharedPtr& pLight) -{ - Scene::SharedPtr pScene = mpSceneRenderer->getScene(); - - // Remove existing light probes - while (pScene->getLightProbeCount() > 0) - { - pScene->deleteLightProbe(0); - } - - pLight->setRadius(pScene->getRadius()); - pLight->setPosW(pScene->getCenter()); - pLight->setSampler(mpSceneSampler); - pScene->addLightProbe(pLight); - - mControls[EnableReflections].enabled = true; - applyLightingProgramControl(ControlID::EnableReflections); -} - -void ForwardRenderer::initAA(SampleCallbacks* pSample) -{ - mTAA.pTAA = TemporalAA::create(); - mpFXAA = FXAA::create(); - applyAaMode(pSample); -} - -void ForwardRenderer::initPostProcess() -{ - mpToneMapper = ToneMapping::create(ToneMapping::Operator::Aces); -} - -void ForwardRenderer::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpState = GraphicsState::create(); - initPostProcess(); - loadScene(pSample, skDefaultScene, true); -} - -void ForwardRenderer::renderSkyBox(RenderContext* pContext) -{ - if (mSkyBox.pEffect) - { - PROFILE("skyBox"); - mpState->setDepthStencilState(mSkyBox.pDS); - mSkyBox.pEffect->render(pContext, mpSceneRenderer->getScene()->getActiveCamera().get()); - mpState->setDepthStencilState(nullptr); - } -} - -void ForwardRenderer::beginFrame(RenderContext* pContext, Fbo* pTargetFbo, uint64_t frameId) -{ - pContext->pushGraphicsState(mpState); - pContext->clearFbo(mpMainFbo.get(), glm::vec4(0.7f, 0.7f, 0.7f, 1.0f), 1, 0, FboAttachmentType::All); - pContext->clearFbo(mpPostProcessFbo.get(), glm::vec4(), 1, 0, FboAttachmentType::Color); - - if (mAAMode == AAMode::TAA) - { - glm::vec2 targetResolution = glm::vec2(pTargetFbo->getWidth(), pTargetFbo->getHeight()); - pContext->clearRtv(mpMainFbo->getColorTexture(2)->getRTV().get(), vec4(0)); - - // Select the sample pattern and set the camera jitter - } -} - -void ForwardRenderer::endFrame(RenderContext* pContext) -{ - pContext->popGraphicsState(); -} - -void ForwardRenderer::postProcess(RenderContext* pContext, Fbo::SharedPtr pTargetFbo) -{ - PROFILE("postProcess"); - mpToneMapper->execute(pContext, mpResolveFbo->getColorTexture(0), pTargetFbo); -} - -void ForwardRenderer::depthPass(RenderContext* pContext) -{ - PROFILE("depthPass"); - if (mEnableDepthPass == false) - { - return; - } - - mpState->setFbo(mpDepthPassFbo); - mpState->setProgram(mDepthPass.pProgram); - pContext->setGraphicsVars(mDepthPass.pVars); - - auto renderMode = mControls[EnableTransparency].enabled ? ForwardRendererSceneRenderer::Mode::Opaque : ForwardRendererSceneRenderer::Mode::All; - mpSceneRenderer->setRenderMode(renderMode); - mpSceneRenderer->renderScene(pContext); -} - -void ForwardRenderer::lightingPass(RenderContext* pContext, Fbo* pTargetFbo) -{ - PROFILE("lightingPass"); - mpState->setProgram(mLightingPass.pProgram); - mpState->setDepthStencilState(mEnableDepthPass ? mLightingPass.pDsState : nullptr); - pContext->setGraphicsVars(mLightingPass.pVars); - ConstantBuffer::SharedPtr pCB = mLightingPass.pVars->getConstantBuffer("PerFrameCB"); - pCB["gOpacityScale"] = mOpacityScale; - - if (mControls[ControlID::EnableShadows].enabled) - { - pCB["camVpAtLastCsmUpdate"] = mShadowPass.camVpAtLastCsmUpdate; - mLightingPass.pVars->setTexture("gVisibilityBuffer", mShadowPass.pVisibilityBuffer); - } - - if (mAAMode == AAMode::TAA) - { - pContext->clearFbo(mTAA.getActiveFbo().get(), vec4(0.0, 0.0, 0.0, 0.0), 1, 0, FboAttachmentType::Color); - pCB["gRenderTargetDim"] = glm::vec2(pTargetFbo->getWidth(), pTargetFbo->getHeight()); - } - - if(mControls[EnableTransparency].enabled) - { - renderOpaqueObjects(pContext); - renderTransparentObjects(pContext); - } - else - { - mpSceneRenderer->setRenderMode(ForwardRendererSceneRenderer::Mode::All); - mpSceneRenderer->renderScene(pContext); - } - pContext->flush(); - mpState->setDepthStencilState(nullptr); -} - -void ForwardRenderer::renderOpaqueObjects(RenderContext* pContext) -{ - mpSceneRenderer->setRenderMode(ForwardRendererSceneRenderer::Mode::Opaque); - mpSceneRenderer->renderScene(pContext); -} - -void ForwardRenderer::renderTransparentObjects(RenderContext* pContext) -{ - mpSceneRenderer->setRenderMode(ForwardRendererSceneRenderer::Mode::Transparent); - mpState->setBlendState(mLightingPass.pAlphaBlendBS); - mpState->setRasterizerState(mLightingPass.pNoCullRS); - mpSceneRenderer->renderScene(pContext); - mpState->setBlendState(nullptr); - mpState->setRasterizerState(nullptr); -} - -void ForwardRenderer::resolveDepthMSAA(RenderContext* pContext) -{ - if (mAAMode == AAMode::MSAA) - { - pContext->resolveResource(mpMainFbo->getDepthStencilTexture(), mpResolveFbo->getColorTexture(2)); - } -} - -void ForwardRenderer::resolveMSAA(RenderContext* pContext) -{ - if(mAAMode == AAMode::MSAA) - { - PROFILE("resolveMSAA"); - pContext->resolveResource(mpMainFbo->getColorTexture(0), mpResolveFbo->getColorTexture(0)); - pContext->resolveResource(mpMainFbo->getColorTexture(1), mpResolveFbo->getColorTexture(1)); - } -} - -void ForwardRenderer::shadowPass(RenderContext* pContext) -{ - PROFILE("shadowPass"); - if (mControls[EnableShadows].enabled && mShadowPass.updateShadowMap) - { - mShadowPass.camVpAtLastCsmUpdate = mpSceneRenderer->getScene()->getActiveCamera()->getViewProjMatrix(); - Texture::SharedPtr pDepth; - if (mAAMode == AAMode::MSAA) - { - pDepth = mpResolveFbo->getColorTexture(2); - } - else - { - pDepth = mpDepthPassFbo->getDepthStencilTexture(); - } - mShadowPass.pVisibilityBuffer = mShadowPass.pCsm->generateVisibilityBuffer(pContext, mpSceneRenderer->getScene()->getActiveCamera().get(), mEnableDepthPass ? pDepth : nullptr); - pContext->flush(); - } -} - -void ForwardRenderer::runTAA(RenderContext* pContext, Fbo::SharedPtr pColorFbo) -{ - if(mAAMode == AAMode::TAA) - { - PROFILE("runTAA"); - // Get the Current Color and Motion Vectors - const Texture::SharedPtr pCurColor = pColorFbo->getColorTexture(0); - const Texture::SharedPtr pMotionVec = mpMainFbo->getColorTexture(2); - - // Get the Previous Color - const Texture::SharedPtr pPrevColor = mTAA.getInactiveFbo()->getColorTexture(0); - - // Execute the Temporal Anti-Aliasing - pContext->getGraphicsState()->pushFbo(mTAA.getActiveFbo()); - mTAA.pTAA->execute(pContext, pCurColor, pPrevColor, pMotionVec); - pContext->getGraphicsState()->popFbo(); - - // Copy over the Anti-Aliased Color Texture - pContext->blit(mTAA.getActiveFbo()->getColorTexture(0)->getSRV(0, 1), pColorFbo->getColorTexture(0)->getRTV()); - - // Swap the Fbos - mTAA.switchFbos(); - } -} - -void ForwardRenderer::ambientOcclusion(RenderContext* pContext, Fbo::SharedPtr pTargetFbo) -{ - PROFILE("ssao"); - if (mControls[EnableSSAO].enabled) - { - Texture::SharedPtr pDepth = (mAAMode == AAMode::MSAA) ? mpResolveFbo->getColorTexture(2) : mpResolveFbo->getDepthStencilTexture(); - Texture::SharedPtr pAOMap = mSSAO.pSSAO->generateAOMap(pContext, mpSceneRenderer->getScene()->getActiveCamera().get(), pDepth, mpResolveFbo->getColorTexture(1)); - mSSAO.pVars->setTexture("gColor", mpPostProcessFbo->getColorTexture(0)); - mSSAO.pVars->setTexture("gAOMap", pAOMap); - - pContext->getGraphicsState()->setFbo(pTargetFbo); - pContext->setGraphicsVars(mSSAO.pVars); - - mSSAO.pApplySSAOPass->execute(pContext); - } -} - -void ForwardRenderer::executeFXAA(RenderContext* pContext, Fbo::SharedPtr pTargetFbo) -{ - PROFILE("fxaa"); - if(mAAMode == AAMode::FXAA) - { - pContext->blit(pTargetFbo->getColorTexture(0)->getSRV(), mpResolveFbo->getRenderTargetView(0)); - mpFXAA->execute(pContext, mpResolveFbo->getColorTexture(0), pTargetFbo); - } -} - -void ForwardRenderer::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - if (mpSceneRenderer) - { - beginFrame(pRenderContext, pTargetFbo.get(), pSample->getFrameID()); - { - PROFILE("updateScene"); - mpSceneRenderer->update(pSample->getCurrentTime()); - } - - depthPass(pRenderContext); - resolveDepthMSAA(pRenderContext); // Only runs in MSAA mode - shadowPass(pRenderContext); - mpState->setFbo(mpMainFbo); - renderSkyBox(pRenderContext); - lightingPass(pRenderContext, pTargetFbo.get()); - resolveMSAA(pRenderContext); // This will only run if we are in MSAA mode - - Fbo::SharedPtr pPostProcessDst = mControls[EnableSSAO].enabled ? mpPostProcessFbo : pTargetFbo; - postProcess(pRenderContext, pPostProcessDst); - runTAA(pRenderContext, pPostProcessDst); // This will only run if we are in TAA mode - ambientOcclusion(pRenderContext, pTargetFbo); - executeFXAA(pRenderContext, pTargetFbo); - - endFrame(pRenderContext); - } - else - { - pRenderContext->clearFbo(pTargetFbo.get(), vec4(0.2f, 0.4f, 0.5f, 1), 1, 0); - } - -} - -void ForwardRenderer::applyCameraPathState() -{ - const Scene* pScene = mpSceneRenderer->getScene().get(); - if(pScene->getPathCount()) - { - mUseCameraPath = mUseCameraPath; - if (mUseCameraPath) - { - pScene->getPath(0)->attachObject(pScene->getActiveCamera()); - } - else - { - pScene->getPath(0)->detachObject(pScene->getActiveCamera()); - } - } -} - -bool ForwardRenderer::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - if (mpSceneRenderer && keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - switch (keyEvent.key) - { - case KeyboardEvent::Key::Minus: - mUseCameraPath = !mUseCameraPath; - applyCameraPathState(); - return true; - case KeyboardEvent::Key::O: - mPerMaterialShader = !mPerMaterialShader; - mpSceneRenderer->toggleStaticMaterialCompilation(mPerMaterialShader); - return true; - } - } - - return mpSceneRenderer ? mpSceneRenderer->onKeyEvent(keyEvent) : false; -} - -void ForwardRenderer::onDroppedFile(SampleCallbacks* pSample, const std::string& filename) -{ - if (hasSuffix(filename, ".fscene", false) == false) - { - msgBox("You can only drop a scene file into the window"); - return; - } - loadScene(pSample, filename, true); -} - -bool ForwardRenderer::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return mpSceneRenderer ? mpSceneRenderer->onMouseEvent(mouseEvent) : true; -} - -void ForwardRenderer::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - // Create the post-process FBO and AA resolve Fbo - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, ResourceFormat::RGBA8UnormSrgb); - mpPostProcessFbo = FboHelper::create2D(width, height, fboDesc); - - applyAaMode(pSample); - mShadowPass.pCsm->onResize(width, height); - - if(mpSceneRenderer) - { - setActiveCameraAspectRatio(width, height); - } -} - -void ForwardRenderer::applyCsSkinningMode() -{ - if(mpSceneRenderer) - { - SkinningCache::SharedPtr pCache = mUseCsSkinning ? SkinningCache::create() : nullptr; - mpSceneRenderer->getScene()->attachSkinningCacheToModels(pCache); - } -} - -void ForwardRenderer::setActiveCameraAspectRatio(uint32_t w, uint32_t h) -{ - mpSceneRenderer->getScene()->getActiveCamera()->setAspectRatio((float)w / (float)h); -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - ForwardRenderer::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Falcor Forward Renderer"; - config.windowDesc.resizableWindow = false; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/ForwardRenderer/ForwardRenderer.h b/Samples/ForwardRenderer/ForwardRenderer.h deleted file mode 100644 index 68ea1680b..000000000 --- a/Samples/ForwardRenderer/ForwardRenderer.h +++ /dev/null @@ -1,218 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" -#include "ForwardRendererSceneRenderer.h" - -using namespace Falcor; - -class ForwardRenderer : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - void onDroppedFile(SampleCallbacks* pSample, const std::string& filename) override; - -private: - Fbo::SharedPtr mpMainFbo; - Fbo::SharedPtr mpDepthPassFbo; - Fbo::SharedPtr mpResolveFbo; - Fbo::SharedPtr mpPostProcessFbo; - - struct ShadowPass - { - bool updateShadowMap = true; - CascadedShadowMaps::SharedPtr pCsm; - Texture::SharedPtr pVisibilityBuffer; - glm::mat4 camVpAtLastCsmUpdate = glm::mat4(); - }; - ShadowPass mShadowPass; - - // SkyBox Pass. - struct - { - SkyBox::SharedPtr pEffect; - DepthStencilState::SharedPtr pDS; - Sampler::SharedPtr pSampler; - } mSkyBox; - - // Lighting Pass. - struct - { - GraphicsVars::SharedPtr pVars; - GraphicsProgram::SharedPtr pProgram; - DepthStencilState::SharedPtr pDsState; - RasterizerState::SharedPtr pNoCullRS; - BlendState::SharedPtr pAlphaBlendBS; - } mLightingPass; - - struct - { - GraphicsVars::SharedPtr pVars; - GraphicsProgram::SharedPtr pProgram; - } mDepthPass; - - - // The Temporal Anti-Aliasing Pass. - class - { - public: - TemporalAA::SharedPtr pTAA; - Fbo::SharedPtr getActiveFbo() { return pTAAFbos[activeFboIndex]; } - Fbo::SharedPtr getInactiveFbo() { return pTAAFbos[1 - activeFboIndex]; } - void createFbos(uint32_t width, uint32_t height, const Fbo::Desc & fboDesc) - { - pTAAFbos[0] = FboHelper::create2D(width, height, fboDesc); - pTAAFbos[1] = FboHelper::create2D(width, height, fboDesc); - } - - void switchFbos() { activeFboIndex = 1 - activeFboIndex; } - void resetFbos() - { - activeFboIndex = 0; - pTAAFbos[0] = nullptr; - pTAAFbos[1] = nullptr; - } - - void resetFboActiveIndex() { activeFboIndex = 0;} - - private: - Fbo::SharedPtr pTAAFbos[2]; - uint32_t activeFboIndex = 0; - } mTAA; - - - ToneMapping::SharedPtr mpToneMapper; - - struct - { - SSAO::SharedPtr pSSAO; - FullScreenPass::UniquePtr pApplySSAOPass; - GraphicsVars::SharedPtr pVars; - } mSSAO; - - FXAA::SharedPtr mpFXAA; - - void beginFrame(RenderContext* pContext, Fbo* pTargetFbo, uint64_t frameId); - void endFrame(RenderContext* pContext); - void depthPass(RenderContext* pContext); - void shadowPass(RenderContext* pContext); - void renderSkyBox(RenderContext* pContext); - void lightingPass(RenderContext* pContext, Fbo* pTargetFbo); - //Need to resolve depth first to pass resolved depth to shadow pass - void resolveDepthMSAA(RenderContext* pContext); - void resolveMSAA(RenderContext* pContext); - void executeFXAA(RenderContext* pContext, Fbo::SharedPtr pTargetFbo); - void runTAA(RenderContext* pContext, Fbo::SharedPtr pColorFbo); - void postProcess(RenderContext* pContext, Fbo::SharedPtr pTargetFbo); - void ambientOcclusion(RenderContext* pContext, Fbo::SharedPtr pTargetFbo); - - - void renderOpaqueObjects(RenderContext* pContext); - void renderTransparentObjects(RenderContext* pContext); - - void initSkyBox(const std::string& name); - void initPostProcess(); - void initLightingPass(); - void initDepthPass(); - void initShadowPass(uint32_t windowWidth, uint32_t windowHeight); - void initSSAO(); - void updateLightProbe(const LightProbe::SharedPtr& pLight); - void initAA(SampleCallbacks* pSample); - - void initControls(); - - GraphicsState::SharedPtr mpState; - ForwardRendererSceneRenderer::SharedPtr mpSceneRenderer; - void loadModel(SampleCallbacks* pSample, const std::string& filename, bool showProgressBar); - void loadScene(SampleCallbacks* pSample, const std::string& filename, bool showProgressBar); - void initScene(SampleCallbacks* pSample, Scene::SharedPtr pScene); - void applyCustomSceneVars(const Scene* pScene, const std::string& filename); - void resetScene(); - - void setActiveCameraAspectRatio(uint32_t w, uint32_t h); - void setSceneSampler(uint32_t maxAniso); - - Sampler::SharedPtr mpSceneSampler; - - struct ProgramControl - { - bool enabled; - bool unsetOnEnabled; - std::string define; - std::string value; - }; - - enum ControlID - { - SuperSampling, - EnableShadows, - EnableReflections, - EnableSSAO, - EnableHashedAlpha, - EnableTransparency, - VisualizeCascades, - Count - }; - - enum class SamplePattern : uint32_t - { - Halton, - DX11 - }; - - enum class AAMode - { - None, - MSAA, - TAA, - FXAA - }; - - float mOpacityScale = 0.5f; - AAMode mAAMode = AAMode::TAA; - uint32_t mMSAASampleCount = 4; - SamplePattern mTAASamplePattern = SamplePattern::Halton; - void applyAaMode(SampleCallbacks* pSample); - std::vector mControls; - void applyLightingProgramControl(ControlID controlID); - - bool mUseCameraPath = true; - void applyCameraPathState(); - bool mPerMaterialShader = false; - bool mEnableDepthPass = true; - bool mUseCsSkinning = false; - void applyCsSkinningMode(); - static const std::string skDefaultScene; - - void createTaaPatternGenerator(uint32_t fboWidth, uint32_t fboHeight); -}; diff --git a/Samples/ForwardRenderer/ForwardRenderer.vcxproj.filters b/Samples/ForwardRenderer/ForwardRenderer.vcxproj.filters deleted file mode 100644 index 7bf997154..000000000 --- a/Samples/ForwardRenderer/ForwardRenderer.vcxproj.filters +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - {392d281b-8330-4036-9268-e190dc6c5dc0} - - - - - Data - - - Data - - - Data - - - \ No newline at end of file diff --git a/Samples/ForwardRenderer/ForwardRendererControls.cpp b/Samples/ForwardRenderer/ForwardRendererControls.cpp deleted file mode 100644 index bbb8bf6c3..000000000 --- a/Samples/ForwardRenderer/ForwardRendererControls.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "ForwardRenderer.h" - -Gui::DropdownList kSampleCountList = -{ - { 2, "2" }, - { 4, "4" }, - { 8, "8" }, -}; - -const Gui::DropdownList aaModeList = -{ - { 0, "None"}, - { 1, "MSAA" }, - { 2, "TAA" }, - { 3, "FXAA" } -}; - - -void ForwardRenderer::initControls() -{ - mControls.resize(ControlID::Count); - mControls[ControlID::SuperSampling] = { false, false, "INTERPOLATION_MODE", "sample" }; - mControls[ControlID::EnableShadows] = { true, false, "_ENABLE_SHADOWS" }; - mControls[ControlID::EnableReflections] = { false, false, "_ENABLE_REFLECTIONS" }; - mControls[ControlID::EnableHashedAlpha] = { true, true, "_DEFAULT_ALPHA_TEST" }; - mControls[ControlID::EnableTransparency] = { false, false, "_ENABLE_TRANSPARENCY" }; - mControls[ControlID::EnableSSAO] = { true, false, "" }; - mControls[ControlID::VisualizeCascades] = { false, false, "_VISUALIZE_CASCADES" }; - - for (uint32_t i = 0; i < ControlID::Count; i++) - { - applyLightingProgramControl((ControlID)i); - } -} - -void ForwardRenderer::applyLightingProgramControl(ControlID controlId) -{ - const ProgramControl control = mControls[controlId]; - if (control.define.size()) - { - bool add = control.unsetOnEnabled ? !control.enabled : control.enabled; - if (add) - { - mLightingPass.pProgram->addDefine(control.define, control.value); - if (controlId == ControlID::EnableHashedAlpha) mDepthPass.pProgram->addDefine(control.define, control.value); - } - else - { - mLightingPass.pProgram->removeDefine(control.define); - if (controlId == ControlID::EnableHashedAlpha) mDepthPass.pProgram->removeDefine(control.define); - } - } -} - -void ForwardRenderer::createTaaPatternGenerator(uint32_t fboWidth, uint32_t fboHeight) -{ - PatternGenerator::SharedPtr pGenerator; - switch (mTAASamplePattern) - { - case SamplePattern::Halton: - pGenerator = HaltonSamplePattern::create(); - break; - case SamplePattern::DX11: - pGenerator = DxSamplePattern::create(); - break; - default: - should_not_get_here(); - pGenerator = nullptr; - } - - mpSceneRenderer->getScene()->getActiveCamera()->setPatternGenerator(pGenerator, 1.0f/vec2(fboWidth, fboHeight)); -} - -void ForwardRenderer::applyAaMode(SampleCallbacks* pSample) -{ - if (mLightingPass.pProgram == nullptr) return; - - assert(mAAMode == AAMode::MSAA ? mMSAASampleCount > 1 : true); - - uint32_t w = pSample->getCurrentFbo()->getWidth(); - uint32_t h = pSample->getCurrentFbo()->getHeight(); - - // Common FBO desc (2 color outputs - color and normal) - Fbo::Desc fboDesc; - fboDesc.setColorTarget(0, ResourceFormat::RGBA32Float).setColorTarget(1, ResourceFormat::RGBA8Unorm).setDepthStencilTarget(ResourceFormat::D32Float); - - // Release the TAA FBOs - mTAA.resetFbos(); - - if (mAAMode == AAMode::TAA) - { - mLightingPass.pProgram->removeDefine("INTERPOLATION_MODE"); - mLightingPass.pProgram->addDefine("_OUTPUT_MOTION_VECTORS"); - fboDesc.setColorTarget(2, ResourceFormat::RG16Float); - - Fbo::Desc taaFboDesc; - taaFboDesc.setColorTarget(0, ResourceFormat::RGBA8UnormSrgb); - mTAA.createFbos(w, h, taaFboDesc); - createTaaPatternGenerator(w, h); - } - else - { - mpSceneRenderer->getScene()->getActiveCamera()->setPatternGenerator(nullptr); - mLightingPass.pProgram->removeDefine("_OUTPUT_MOTION_VECTORS"); - applyLightingProgramControl(SuperSampling); - fboDesc.setSampleCount(mAAMode == AAMode::MSAA ? mMSAASampleCount : 1); - - if(mAAMode == AAMode::MSAA) - { - Fbo::Desc resolveDesc; - resolveDesc.setColorTarget(0, ResourceFormat::RGBA32Float); - resolveDesc.setColorTarget(1, ResourceFormat::RGBA8Unorm).setColorTarget(2, ResourceFormat::R32Float); - mpResolveFbo = FboHelper::create2D(w, h, resolveDesc); - } - else if (mAAMode == AAMode::FXAA) - { - Fbo::Desc resolveDesc; - resolveDesc.setColorTarget(0, pSample->getCurrentFbo()->getColorTexture(0)->getFormat()); - mpResolveFbo = FboHelper::create2D(w, h, resolveDesc); - } - } - - mpMainFbo = FboHelper::create2D(w, h, fboDesc); - mpDepthPassFbo = Fbo::create(); - mpDepthPassFbo->attachDepthStencilTarget(mpMainFbo->getDepthStencilTexture()); - - if (mAAMode != AAMode::MSAA) - { - mpResolveFbo = mpMainFbo; - } -} - -void ForwardRenderer::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - static const FileDialogFilterVec kImageFilesFilter = { {"bmp"}, {"jpg"}, {"dds"}, {"png"}, {"tiff"}, {"tif"}, {"tga"} }; - - if (pGui->addButton("Load Model")) - { - std::string filename; - if (openFileDialog(Model::kFileExtensionFilters, filename)) - { - loadModel(pSample, filename, true); - } - } - - if (pGui->addButton("Load Scene")) - { - std::string filename; - if (openFileDialog(Scene::kFileExtensionFilters, filename)) - { - loadScene(pSample, filename, true); - } - } - - if (mpSceneRenderer) - { - if (pGui->addButton("Load SkyBox Texture")) - { - std::string filename; - if (openFileDialog(kImageFilesFilter, filename)) - { - initSkyBox(filename); - } - } - - if (pGui->beginGroup("Scene Settings")) - { - Scene* pScene = mpSceneRenderer->getScene().get(); - float camSpeed = pScene->getCameraSpeed(); - if (pGui->addFloatVar("Camera Speed", camSpeed)) - { - pScene->setCameraSpeed(camSpeed); - } - - vec2 depthRange(pScene->getActiveCamera()->getNearPlane(), pScene->getActiveCamera()->getFarPlane()); - if (pGui->addFloat2Var("Depth Range", depthRange, 0, FLT_MAX)) - { - pScene->getActiveCamera()->setDepthRange(depthRange.x, depthRange.y); - } - - if (pScene->getPathCount() > 0) - { - if (pGui->addCheckBox("Camera Path", mUseCameraPath)) - { - applyCameraPathState(); - } - } - - if (pScene->getLightCount() && pGui->beginGroup("Light Sources")) - { - for (uint32_t i = 0; i < pScene->getLightCount(); i++) - { - Light* pLight = pScene->getLight(i).get(); - pLight->renderUI(pGui, pLight->getName().c_str()); - } - pGui->endGroup(); - } - - if (pGui->addCheckBox("Use CS for Skinning", mUseCsSkinning)) - { - applyCsSkinningMode(); - } - pGui->endGroup(); - } - - if (pGui->beginGroup("Renderer Settings")) - { - pGui->addCheckBox("Depth Pass", mEnableDepthPass); - pGui->addTooltip("Run a depth-pass at the beginning of the frame"); - - if (pGui->addCheckBox("Specialize Material Shaders", mPerMaterialShader)) - { - mpSceneRenderer->toggleStaticMaterialCompilation(mPerMaterialShader); - } - pGui->addTooltip("Create a specialized version of the lighting program for each material in the scene"); - - uint32_t maxAniso = mpSceneSampler->getMaxAnisotropy(); - if (pGui->addIntVar("Max Anisotropy", (int&)maxAniso, 1, 16)) - { - setSceneSampler(maxAniso); - } - - pGui->endGroup(); - } - - // Anti-Aliasing Controls. - if (pGui->beginGroup("Anti-Aliasing")) - { - bool reapply = false; - reapply = reapply || pGui->addDropdown("AA Mode", aaModeList, (uint32_t&)mAAMode); - - if (mAAMode == AAMode::MSAA) - { - reapply = reapply || pGui->addDropdown("Sample Count", kSampleCountList, mMSAASampleCount); - - if (pGui->addCheckBox("Super Sampling", mControls[ControlID::SuperSampling].enabled)) - { - applyLightingProgramControl(ControlID::SuperSampling); - } - } - - // Temporal Anti-Aliasing. - if (mAAMode == AAMode::TAA) - { - if (pGui->beginGroup("TAA")) - { - // Render the TAA UI. - mTAA.pTAA->renderUI(pGui); - - // Choose the Sample Pattern for TAA. - Gui::DropdownList samplePatternList; - samplePatternList.push_back({ (uint32_t)SamplePattern::Halton, "Halton" }); - samplePatternList.push_back({ (uint32_t)SamplePattern::DX11, "DX11" }); - pGui->addDropdown("Sample Pattern", samplePatternList, (uint32_t&)mTAASamplePattern); - - // Disable super-sampling - pGui->endGroup(); - } - } - - if (mAAMode == AAMode::FXAA) - { - mpFXAA->renderUI(pGui, "FXAA"); - } - - if (reapply) applyAaMode(pSample); - - pGui->endGroup(); - } - - if (pGui->beginGroup("Light Probes")) - { - if (pGui->addButton("Add/Change Light Probe")) - { - std::string filename; - if (openFileDialog(kImageFilesFilter, filename)) - { - updateLightProbe(LightProbe::create(pSample->getRenderContext(), filename, true, ResourceFormat::RGBA16Float)); - } - } - - Scene::SharedPtr pScene = mpSceneRenderer->getScene(); - if (pScene->getLightProbeCount() > 0) - { - if (pGui->addCheckBox("Enable", mControls[ControlID::EnableReflections].enabled)) - { - applyLightingProgramControl(ControlID::EnableReflections); - } - if (mControls[ControlID::EnableReflections].enabled) - { - pGui->addSeparator(); - pScene->getLightProbe(0)->renderUI(pGui); - } - } - - pGui->endGroup(); - } - - mpToneMapper->renderUI(pGui, "Tone-Mapping"); - - if (pGui->beginGroup("Shadows")) - { - if (pGui->addCheckBox("Enable Shadows", mControls[ControlID::EnableShadows].enabled)) - { - applyLightingProgramControl(ControlID::EnableShadows); - } - if (mControls[ControlID::EnableShadows].enabled) - { - pGui->addCheckBox("Update Map", mShadowPass.updateShadowMap); - mShadowPass.pCsm->renderUI(pGui); - if (pGui->addCheckBox("Visualize Cascades", mControls[ControlID::VisualizeCascades].enabled)) - { - applyLightingProgramControl(ControlID::VisualizeCascades); - mShadowPass.pCsm->toggleCascadeVisualization(mControls[ControlID::VisualizeCascades].enabled); - } - } - pGui->endGroup(); - } - - if (pGui->beginGroup("SSAO")) - { - if (pGui->addCheckBox("Enable SSAO", mControls[ControlID::EnableSSAO].enabled)) - { - applyLightingProgramControl(ControlID::EnableSSAO); - } - - if (mControls[ControlID::EnableSSAO].enabled) - { - mSSAO.pSSAO->renderUI(pGui); - } - pGui->endGroup(); - } - - if (pGui->beginGroup("Transparency")) - { - if (pGui->addCheckBox("Enable Transparency", mControls[ControlID::EnableTransparency].enabled)) - { - applyLightingProgramControl(ControlID::EnableTransparency); - } - pGui->addFloatVar("Opacity Scale", mOpacityScale, 0, 1); - pGui->endGroup(); - } - - if (pGui->addCheckBox("Hashed-Alpha Test", mControls[ControlID::EnableHashedAlpha].enabled)) - { - applyLightingProgramControl(ControlID::EnableHashedAlpha); - } - } -} diff --git a/Samples/ForwardRenderer/ForwardRendererSceneRenderer.cpp b/Samples/ForwardRenderer/ForwardRendererSceneRenderer.cpp deleted file mode 100644 index a593a63ad..000000000 --- a/Samples/ForwardRenderer/ForwardRendererSceneRenderer.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "ForwardRendererSceneRenderer.h" - -static bool isMaterialTransparent(const Material* pMaterial) -{ - return pMaterial->getBaseColor().a < 1.0f; -} - -ForwardRendererSceneRenderer::ForwardRendererSceneRenderer(const Scene::SharedPtr& pScene) : SceneRenderer(pScene) -{ - for (uint32_t model = 0; model < mpScene->getModelCount(); model++) - { - const auto& pModel = mpScene->getModel(model); - for (uint32_t mesh = 0; mesh < pModel->getMeshCount(); mesh++) - { - const auto& pMesh = pModel->getMesh(mesh); - uint32_t id = pMesh->getId(); - if (mTransparentMeshes.size() <= id) mTransparentMeshes.resize(id + 1); - bool transparent = isMaterialTransparent(pMesh->getMaterial().get()); - mHasOpaqueObjects = mHasOpaqueObjects || (transparent == false); - mHasTransparentObject = mHasTransparentObject || transparent; - mTransparentMeshes[id] = transparent; - } - } - - RasterizerState::Desc rsDesc; - mpDefaultRS = RasterizerState::create(rsDesc); - rsDesc.setCullMode(RasterizerState::CullMode::None); - mpNoCullRS = RasterizerState::create(rsDesc); -} - -ForwardRendererSceneRenderer::SharedPtr ForwardRendererSceneRenderer::create(const Scene::SharedPtr& pScene) -{ - return SharedPtr(new ForwardRendererSceneRenderer(pScene)); -} - -bool ForwardRendererSceneRenderer::setPerMeshData(const CurrentWorkingData& currentData, const Mesh* pMesh) -{ - switch (mRenderMode) - { - case Mode::All: - return true; - case Mode::Opaque: - return mTransparentMeshes[pMesh->getId()] == false; - case Mode::Transparent: - return mTransparentMeshes[pMesh->getId()]; - default: - should_not_get_here(); - return false; - } -} - -void ForwardRendererSceneRenderer::renderScene(RenderContext* pContext) -{ - switch (mRenderMode) - { - case Mode::Opaque: - if (mHasOpaqueObjects == false) return; - break; - case Mode::Transparent: - if (mHasTransparentObject == false) return; - } - SceneRenderer::renderScene(pContext); -} - -RasterizerState::SharedPtr ForwardRendererSceneRenderer::getRasterizerState(const Material* pMaterial) -{ - if (pMaterial->getAlphaMode() == AlphaModeMask) - { - return mpNoCullRS; - } - else - { - return mpDefaultRS; - } -} - -bool ForwardRendererSceneRenderer::setPerMaterialData(const CurrentWorkingData& currentData, const Material* pMaterial) -{ - const auto& pRsState = getRasterizerState(currentData.pMaterial); - if (pRsState != mpLastSetRs) - { - currentData.pContext->getGraphicsState()->setRasterizerState(pRsState); - mpLastSetRs = pRsState; - } - - return SceneRenderer::setPerMaterialData(currentData, pMaterial); -} \ No newline at end of file diff --git a/Samples/ForwardRenderer/ForwardRendererSceneRenderer.h b/Samples/ForwardRenderer/ForwardRendererSceneRenderer.h deleted file mode 100644 index 7f2a399b7..000000000 --- a/Samples/ForwardRenderer/ForwardRendererSceneRenderer.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class ForwardRendererSceneRenderer : public SceneRenderer -{ -public: - using SharedPtr = std::shared_ptr; - ~ForwardRendererSceneRenderer() = default; - enum class Mode - { - All, - Opaque, - Transparent - }; - - static SharedPtr create(const Scene::SharedPtr& pScene); - void setRenderMode(Mode renderMode) { mRenderMode = renderMode; } - void renderScene(RenderContext* pContext) override; -private: - bool setPerMeshData(const CurrentWorkingData& currentData, const Mesh* pMesh) override; - bool setPerMaterialData(const CurrentWorkingData& currentData, const Material* pMaterial) override; - RasterizerState::SharedPtr getRasterizerState(const Material* pMaterial); - ForwardRendererSceneRenderer(const Scene::SharedPtr& pScene); - std::vector mTransparentMeshes; - Mode mRenderMode = Mode::All; - bool mHasOpaqueObjects = false; - bool mHasTransparentObject = false; - - RasterizerState::SharedPtr mpDefaultRS; - RasterizerState::SharedPtr mpNoCullRS; - RasterizerState::SharedPtr mpLastSetRs; -}; diff --git a/Samples/Raytracing/PathTracer/Data/GGXGICommon.slang b/Samples/Raytracing/PathTracer/Data/GGXGICommon.slang deleted file mode 100644 index 949143dcc..000000000 --- a/Samples/Raytracing/PathTracer/Data/GGXGICommon.slang +++ /dev/null @@ -1,224 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -import ShaderCommon; -import Raytracing; -import Shading; -import BRDF; -import Lights; -import Helpers; -#include "HostDeviceSharedMacros.h" -#include "HostDeviceData.h" - -// A constant buffer we'll populate from our C++ code (used for our ray generation shader) -shared cbuffer GlobalCB -{ - float gMinT; // Min distance to start a ray to avoid self-occlusion - uint gFrameCount; // An integer changing every frame to update the random number - bool gDoIndirectGI; // A boolean determining if we should shoot indirect GI rays - bool gDoDirectGI; // A boolean determining if we should compute direct lighting - uint gMaxDepth; // Maximum number of recursive bounces to allow - float gEmitMult; // Emissives are currently hacked. Multiply emissive amount by this factor -} - -shared ITexture2D gPos; -shared ITexture2D gNorm; -shared ITexture2D gDiffuseMatl; -shared ITexture2D gSpecMatl; -shared ITexture2D gExtraMatl; -shared ITexture2D gEmissive; -shared IRWTexture2D gOutput; -shared ITexture2D gEnvMap; - -// Payload for our shadow rays. -struct ShadowRayPayload -{ - float visFactor; // Will be 1.0 for fully lit, 0.0 for fully shadowed -}; - -// The payload structure for our indirect rays -struct IndirectRayPayload -{ - float3 color; // The (returned) color in the ray's direction - uint rndSeed; // Our random seed, so we pick uncorrelated RNGs along our ray - uint rayDepth; // What is the depth of our current ray? -}; - -/** Returns true if surface is transparent -*/ -bool evalRtAlphaTest(BuiltInTriangleIntersectionAttributes attribs) -{ - VertexOut vsOut = getVertexAttributes(PrimitiveIndex(), attribs); - - // Extracts the diffuse color from the material (the alpha component is opacity) - ExplicitLodTextureSampler lodSampler = { 0 }; - float4 baseColor = sampleTexture(gMaterial.resources.baseColor, gMaterial.resources.samplerState, - vsOut.texC, gMaterial.baseColor, EXTRACT_DIFFUSE_TYPE(gMaterial.flags), lodSampler); - - return evalBasicAlphaTest(baseColor.a, gMaterial.alphaThreshold); -} - -/** Shoots a shadow ray using given parameters. Assumes hit group #0 and miss shader #0; - \return Visibility factor: 1 if no shadow, and 0 if shadowed. -*/ -float shootShadowRay(float3 origin, float3 direction, float minT, float maxT) -{ - RayDesc ray; - ray.Origin = origin; - ray.Direction = direction; - ray.TMin = minT; - ray.TMax = maxT; - - // Our shadow rays are *assumed* to hit geometry; this miss shader changes this to 1.0 for "visible" - ShadowRayPayload payload = { 0.0f }; - - // Query if anything is between the current point and the light - TraceRay(gRtScene, - RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER, - 0xFF, 0, hitProgramCount, 0, ray, payload); - - // Return our ray payload (which is 1 for visible, 0 for occluded) - return payload.visFactor; -} - -/** Evaluates direct lighting and shadow from a single randomly selected light in the scene. - - \return Final evaluated color -*/ -float3 ggxDirect(inout uint rndSeed, ShadingData sd) -{ - // Pick a random light from our scene to sample - int lightIndex = min(int(rand_next(rndSeed) * gLightsCount), gLightsCount - 1); - - LightData ld = gLights[lightIndex]; - ShadingResult sr = evalMaterial(sd, gLights[lightIndex], 1.0f); - - // Shoot our shadow ray to our randomly selected light - float distToLight = length(ld.posW - sd.posW); - float3 L = -normalize(ld.dirW); - float shadowMult = float(gLightsCount) * shootShadowRay(sd.posW, L, gMinT, distToLight); - - return shadowMult * sr.color.rgb; -} - -/** Helper for shooting a ray for indirect lighting. Assumes hit group #1 and miss shader #1 - - \return Color sampled by the ray. -*/ -float3 shootIndirectRay(float3 rayOrigin, float3 rayDir, float minT, uint curPathLen, uint seed, uint curDepth) -{ - // Setup our indirect ray - RayDesc rayColor; - rayColor.Origin = rayOrigin; - rayColor.Direction = rayDir; - rayColor.TMin = minT; - rayColor.TMax = 1.0e38f; - - // Initialize the ray's payload data with black return color and the current rng seed - IndirectRayPayload payload; - payload.color = float3(0, 0, 0); - payload.rndSeed = seed; - payload.rayDepth = curDepth + 1; - - // Trace our ray to get a color in the indirect direction. Use hit group #1 and miss shader #1 - TraceRay(gRtScene, 0, 0xFF, 1, hitProgramCount, 1, rayColor, payload); - - // Return the color we got from our ray - return payload.color; -} - -/** Our material has have both a diffuse and a specular lobe. - Calculates what probability the diffuse one should be sampled. - - \return Probability between 0 and 1 -*/ -float probabilityToSampleDiffuse(float3 difColor, float3 specColor) -{ - float lumDiffuse = max(0.01f, luminance(difColor.rgb)); - float lumSpecular = max(0.01f, luminance(specColor.rgb)); - return lumDiffuse / (lumDiffuse + lumSpecular); -} - -/** Evaluates indirect lighting up to a bounce count specified by gMaxDepth. Initial call should leave rayDepth = 0. -*/ -float3 ggxIndirect(inout uint rndSeed, ShadingData sd, float3 geomN, uint rayDepth = 0) -{ - // We have to decide whether we sample our diffuse or specular/ggx lobe. - float probDiffuse = probabilityToSampleDiffuse(sd.diffuse, sd.specular); - float chooseDiffuse = (rand_next(rndSeed) < probDiffuse); - float2 randVal = float2(rand_next(rndSeed), rand_next(rndSeed)); - - // We'll need NdotV for both diffuse and specular - float NdotV = saturate(dot(sd.N, sd.V)); - - // If we randomly selected to sample our diffuse lobe - if (chooseDiffuse) - { - // Shoot a randomly selected cosine-sampled diffuse ray. - float3 L = getCosHemisphereSample(randVal, sd.N, getPerpendicularStark(sd.N)); - float3 bounceColor = shootIndirectRay(sd.posW, L, gMinT, 0, rndSeed, rayDepth); - - // Check to make sure our randomly selected, normal mapped diffuse ray didn't go below the surface. - if (dot(geomN, L) <= 0.0f) bounceColor = float3(0, 0, 0); - - // Accumulate the color: (NdotL * incomingLight * diff / pi) - // Probability of sampling: (NdotL / pi) * probDiffuse - return bounceColor * sd.diffuse / probDiffuse; - } - // Otherwise we randomly selected to sample our GGX lobe - else - { - // Randomly sample the NDF to get a microfacet in our BRDF to reflect off of - float3 H = getGGXMicrofacet(randVal, sd.N, sd.roughness); - - // Compute the outgoing direction based on this (perfectly reflective) microfacet - float3 L = reflect(-sd.V, H); - - // Compute our color by tracing a ray in this direction - float3 bounceColor = shootIndirectRay(sd.posW, L, gMinT, 0, rndSeed, rayDepth); - - // Check to make sure our randomly selected, normal mapped diffuse ray didn't go below the surface. - if (dot(geomN, L) <= 0.0f) bounceColor = float3(0, 0, 0); - - // Compute some dot products needed for shading - float NdotL = saturate(dot(sd.N, L)); - float NdotH = saturate(dot(sd.N, H)); - float LdotH = saturate(dot(L, H)); - - // Cannot use evalSpecularBrdf() because we need the D term below - float D = evalGGX(sd.roughness, NdotH) * M_INV_PI; // Our GGX function does not include division by PI - float G = evalSmithGGX(NdotL, NdotV, sd.roughness); // Includes division by 4 * NdotL * NdotV - float3 F = fresnelSchlick(sd.specular, 1, max(0, LdotH)); - float3 brdf = D * G * F; - - // Probability of sampling vector H from getGGXMicrofacet() - float ggxProb = D * NdotH / (4 * LdotH); - - // Accumulate the color: ggx-BRDF * incomingLight * NdotL / probability-of-sampling - return NdotL * bounceColor * brdf / (ggxProb * (1.0f - probDiffuse)); - } -} diff --git a/Samples/Raytracing/PathTracer/Data/GGXGIRayGen.slang b/Samples/Raytracing/PathTracer/Data/GGXGIRayGen.slang deleted file mode 100644 index c2f51e2f9..000000000 --- a/Samples/Raytracing/PathTracer/Data/GGXGIRayGen.slang +++ /dev/null @@ -1,93 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -import ShaderCommon; -import Shading; -import Helpers; -import GGXGICommon; - -[shader("raygeneration")] -void GGXGlobalIllumRayGen() -{ - uint2 launchIndex = DispatchRaysIndex().xy; - uint2 launchDim = DispatchRaysDimensions().xy; - - // Initialize our random number generator - uint randSeed = rand_init(launchIndex.x + launchIndex.y * launchDim.x, gFrameCount, 16); - - // Load g-buffer data - float4 posW = gPos[launchIndex]; - float3 normW = gNorm[launchIndex].xyz; - float4 diffuse = gDiffuseMatl[launchIndex]; - float4 specRough = gSpecMatl[launchIndex]; - float4 emissive = gEmissive[launchIndex]; - float3 geomN = gExtraMatl[launchIndex].xyz; - - // Does this g-buffer pixel contain a valid piece of geometry? (0 in pos.w for invalid) - bool isGeometryValid = (posW.w != 0.0f); - - // Extract and compute some material and geometric parameters - float roughness = specRough.a * specRough.a; - float3 V = normalize(gCamera.posW - posW.xyz); - - // Make sure our normal is pointed the right direction - if (dot(normW, V) <= 0.0f) normW = -normW; - - // Make sure out geometric normal is pointed in the right direction - if (dot(geomN, V) <= 0.0f) geomN = -geomN; - - float3 shadeColor = float3(0, 0, 0); - - // Do shading, if we have geometry here (otherwise, output the background color) - if (isGeometryValid) - { - // Add any emissive color from primary rays - shadeColor = gEmitMult * emissive.rgb; - - // Fill out ShadingData struct with GBuffer data - ShadingData sd = initShadingData(); - sd.posW = posW.xyz; - sd.N = normW; - sd.V = V; - sd.diffuse = diffuse.rgb; - sd.specular = specRough.rgb; - sd.linearRoughness = specRough.a; - sd.roughness = roughness; - - // Direct lighting to a random light in the scene - if (gDoDirectGI) shadeColor += ggxDirect(randSeed, sd); - - // Indirect lighting for global illumination - if (gDoIndirectGI && (gMaxDepth > 0)) shadeColor += ggxIndirect(randSeed, sd, geomN); - - // Zero out any bad samples - bool colorsNan = any(isnan(shadeColor)); - shadeColor = colorsNan ? float3(0, 0, 0) : shadeColor; - } - - gOutput[launchIndex] = float4(shadeColor, 1.0f); -} diff --git a/Samples/Raytracing/PathTracer/PathTracer.cpp b/Samples/Raytracing/PathTracer/PathTracer.cpp deleted file mode 100644 index 25f93757a..000000000 --- a/Samples/Raytracing/PathTracer/PathTracer.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "PathTracer.h" -#include "RenderPasses/GGXGlobalIllumination.h" -#include "RenderPasses/GBufferRaster.h" -#include "RenderPasses/TemporalAccumulation.h" - -void PathTracer::onGuiRender(SampleCallbacks* pCallbacks, Gui* pGui) -{ - if (pGui->addButton("Load Scene")) - { - assert(mpGraph != nullptr); - std::string filename; - if (openFileDialog(Scene::kFileExtensionFilters, filename)) - { - ProgressBar::SharedPtr pBar = ProgressBar::create("Loading Scene", 100); - - RtScene::SharedPtr pScene = RtScene::loadFromFile(filename); - if (pScene != nullptr) - { - Fbo::SharedPtr pFbo = pCallbacks->getCurrentFbo(); - pScene->setCamerasAspectRatio(float(pFbo->getWidth()) / float(pFbo->getHeight())); - mpGraph->setScene(pScene); - } - } - } - - pGui->addSeparator(); - - if (pGui->addButton(mDisableCameraPath ? "Enable Camera Path" : "Disable Camera Path")) - { - toggleCameraPathState(); - } - - if (mpGraph != nullptr) - { - mpGraph->renderUI(pGui, nullptr); - } -} - -void PathTracer::toggleCameraPathState() -{ - Scene::SharedPtr pScene = mpGraph->getScene(); - if (pScene != nullptr && pScene->getPathCount() > 0) - { - mDisableCameraPath = !mDisableCameraPath; - if (mDisableCameraPath) - { - pScene->getPath(0)->detachObject(pScene->getActiveCamera()); - } - else - { - pScene->getPath(0)->attachObject(pScene->getActiveCamera()); - } - } -} - -void PathTracer::onLoad(SampleCallbacks* pCallbacks, RenderContext* pRenderContext) -{ - mpGraph = RenderGraph::create("Path Tracer"); - mpGraph->addPass(GBufferRaster::create(), "GBuffer"); - auto pGIPass = GGXGlobalIllumination::create(); - mpGraph->addPass(pGIPass, "GlobalIllumination"); - mpGraph->addPass(TemporalAccumulation::create(), "TemporalAccumulation"); - mpGraph->addPass(ToneMapping::create(), "ToneMapping"); - - mpGraph->addEdge("GBuffer.posW", "GlobalIllumination.posW"); - mpGraph->addEdge("GBuffer.normW", "GlobalIllumination.normW"); - mpGraph->addEdge("GBuffer.diffuseOpacity", "GlobalIllumination.diffuseOpacity"); - mpGraph->addEdge("GBuffer.specRough", "GlobalIllumination.specRough"); - mpGraph->addEdge("GBuffer.emissive", "GlobalIllumination.emissive"); - mpGraph->addEdge("GBuffer.matlExtra", "GlobalIllumination.matlExtra"); - - mpGraph->addEdge("GlobalIllumination.output", "TemporalAccumulation.input"); - - mpGraph->addEdge("TemporalAccumulation.output", "ToneMapping.src"); - - mpGraph->markOutput("ToneMapping.dst"); - - // When GI pass changes, tell temporal accumulation to reset - pGIPass->setPassChangedCB([this]() {(*mpGraph->getPassesDictionary())["_dirty"] = true; }); - - // Initialize the graph's record of what the swapchain size is, for texture creation - mpGraph->onResize(pCallbacks->getCurrentFbo().get()); - - { - ProgressBar::SharedPtr pBar = ProgressBar::create("Loading Scene", 100); - - RtScene::SharedPtr pScene = RtScene::loadFromFile("Arcade/Arcade.fscene"); - if (pScene != nullptr) - { - Fbo::SharedPtr pFbo = pCallbacks->getCurrentFbo(); - pScene->setCamerasAspectRatio(float(pFbo->getWidth()) / float(pFbo->getHeight())); - mpGraph->setScene(pScene); - } - } -} - -void PathTracer::onFrameRender(SampleCallbacks* pCallbacks, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - if (mpGraph->getScene() != nullptr) - { - mpGraph->getScene()->update(pCallbacks->getCurrentTime(), &mCamController); - mpGraph->execute(pRenderContext); - - pRenderContext->blit(mpGraph->getOutput("ToneMapping.dst")->getSRV(), pTargetFbo->getRenderTargetView(0)); - } -} - -void PathTracer::onShutdown(SampleCallbacks* pCallbacks) -{ -} - -bool PathTracer::onKeyEvent(SampleCallbacks* pCallbacks, const KeyboardEvent& keyEvent) -{ - if (keyEvent.key == KeyboardEvent::Key::Minus && keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - toggleCameraPathState(); - return true; - } - - bool handled = false; - if (mpGraph->getScene() != nullptr) handled = mpGraph->onKeyEvent(keyEvent); - return handled ? true : mCamController.onKeyEvent(keyEvent); -} - -bool PathTracer::onMouseEvent(SampleCallbacks* pCallbacks, const MouseEvent& mouseEvent) -{ - bool handled = false; - if (mpGraph->getScene() != nullptr) handled = mpGraph->onMouseEvent(mouseEvent); - return handled ? true : mCamController.onMouseEvent(mouseEvent); -} - -void PathTracer::onDataReload(SampleCallbacks* pCallbacks) -{ - -} - -void PathTracer::onResizeSwapChain(SampleCallbacks* pCallbacks, uint32_t width, uint32_t height) -{ - if (mpGraph) - { - mpGraph->onResize(pCallbacks->getCurrentFbo().get()); - if(mpGraph->getScene() != nullptr) mpGraph->getScene()->setCamerasAspectRatio((float)width / (float)height); - } -} - -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -{ - PathTracer::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Path Tracer"; - config.windowDesc.resizableWindow = true; - config.freezeTimeOnStartup = true; - Sample::run(config, pRenderer); - return 0; -} diff --git a/Samples/Raytracing/PathTracer/PathTracer.vcxproj.filters b/Samples/Raytracing/PathTracer/PathTracer.vcxproj.filters deleted file mode 100644 index 24748f0ab..000000000 --- a/Samples/Raytracing/PathTracer/PathTracer.vcxproj.filters +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - RenderPasses\GGXGlobalIllumination - - - RenderPasses\TemporalAccumulation - - - RenderPasses\GBuffer - - - - - - RenderPasses\GGXGlobalIllumination - - - RenderPasses\TemporalAccumulation - - - RenderPasses\GBuffer - - - RenderPasses\GBuffer - - - - - {b6a8a680-8843-4162-9110-beb7c61c98c3} - - - {b2788f57-7ebf-40c0-b553-3102f2279bf9} - - - {90b33fce-7aa1-4ae1-be85-2bac5f88c8ce} - - - {c3473781-ddb5-4597-971a-be0daf6bb798} - - - {f060b8f2-6026-499b-9047-99d2ccba11e6} - - - {929740ec-b9db-4ac0-ba45-b444f34fc1c2} - - - {8a6d968b-234a-4d7a-a6ba-3f78bc3e4018} - - - {9159f8eb-dbfe-493c-8d7c-752459cc0aa0} - - - - - Data\GBuffer - - - Data\TemporalAccumulation - - - Data\GGXGlobalIllumination - - - Data\GGXGlobalIllumination - - - Data\GGXGlobalIllumination - - - Data\GGXGlobalIllumination - - - \ No newline at end of file diff --git a/Samples/Raytracing/PathTracer/RenderPasses/GBuffer.h b/Samples/Raytracing/PathTracer/RenderPasses/GBuffer.h deleted file mode 100644 index 70d209cb7..000000000 --- a/Samples/Raytracing/PathTracer/RenderPasses/GBuffer.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -// GBuffer channel metadata -struct GBufferChannelDesc -{ - const char* name; // Canonical channel name - const char* desc; // Human-readable channel description - const char* texname; // Name of corresponding ITexture2D in GBufferRT shader code -}; - -// Note that channel order should correspond to SV_TARGET index order used in -// GBufferRaster's primary fragment shader. -static const std::vector kGBufferChannelDesc({ - {"posW", "world space position", "gPosW" }, - {"normW", "world space normal", "gNormW" }, - {"bitangentW", "world space bitangent", "gBitangentW" }, - {"texC", "texture coordinates", "gTexC" }, - {"diffuseOpacity", "diffuse color and opacity", "gDiffuseOpacity" }, - {"specRough", "specular color and roughness", "gSpecRough" }, - {"emissive", "emissive color", "gEmissive" }, - {"matlExtra", "additional material data", "gMatlExtra" } - }); - -// Culling dictionary key and dropdown mode selection -static const std::string kCull = "cull"; - -static const Falcor::Gui::DropdownList kCullModeList = -{ - { (uint32_t)Falcor::RasterizerState::CullMode::None, "None" }, - { (uint32_t)Falcor::RasterizerState::CullMode::Back, "Back" }, - { (uint32_t)Falcor::RasterizerState::CullMode::Front, "Front" }, -}; - diff --git a/Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.cpp b/Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.cpp deleted file mode 100644 index 0689e53f6..000000000 --- a/Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "Framework.h" -#include "GBuffer.h" -#include "GBufferRaster.h" - -namespace -{ - const char kFileRasterPrimary[] = "RasterPrimary.slang"; -} - -RenderPassReflection GBufferRaster::reflect() const -{ - RenderPassReflection r; - for (int i = 0; i < kGBufferChannelDesc.size(); ++i) - { - r.addOutput(kGBufferChannelDesc[i].name, kGBufferChannelDesc[i].desc).format(ResourceFormat::RGBA32Float).bindFlags(Resource::BindFlags::RenderTarget); - } - r.addOutput("depthStencil", "depth and stencil").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil); - return r; -} - -bool GBufferRaster::parseDictionary(const Dictionary& dict) -{ - for (const auto& v : dict) - { - if (v.key() == kCull) - { - setCullMode((RasterizerState::CullMode)v.val()); - } - else - { - logWarning("Unknown field `" + v.key() + "` in a GBufferRaster dictionary"); - } - } - return true; -} - -GBufferRaster::SharedPtr GBufferRaster::create(const Dictionary& dict) -{ - SharedPtr pPass = SharedPtr(new GBufferRaster); - return pPass->parseDictionary(dict) ? pPass : nullptr; -} - -Dictionary GBufferRaster::getScriptingDictionary() const -{ - Dictionary dict; - dict[kCull] = mCullMode; - return dict; -} - -GBufferRaster::GBufferRaster() : RenderPass("GBufferRaster") -{ - mpGraphicsState = GraphicsState::create(); - - mRaster.pProgram = GraphicsProgram::createFromFile(kFileRasterPrimary, "", "ps"); - - // Initialize graphics state - mRaster.pState = GraphicsState::create(); - - // Set default culling mode - setCullMode(mCullMode); - - mRaster.pVars = GraphicsVars::create(mRaster.pProgram->getReflector()); - mRaster.pState->setProgram(mRaster.pProgram); - - mpFbo = Fbo::create(); -} - -void GBufferRaster::onResize(uint32_t width, uint32_t height) -{ -} - -void GBufferRaster::setScene(const std::shared_ptr& pScene) -{ - mpSceneRenderer = (pScene == nullptr) ? nullptr : SceneRenderer::create(pScene); -} - -void GBufferRaster::renderUI(Gui* pGui, const char* uiGroup) -{ - uint32_t cullMode = (uint32_t)mCullMode; - if (pGui->addDropdown("Cull Mode", kCullModeList, cullMode)) - { - setCullMode((RasterizerState::CullMode)cullMode); - } -} - -void GBufferRaster::setCullMode(RasterizerState::CullMode mode) -{ - mCullMode = mode; - RasterizerState::Desc rsDesc; - rsDesc.setCullMode(mCullMode); - mRaster.pState->setRasterizerState(RasterizerState::create(rsDesc)); -} - - -void GBufferRaster::execute(RenderContext* pContext, const RenderData* pRenderData) -{ - if (mpSceneRenderer == nullptr) - { - logWarning("Invalid SceneRenderer in GBufferRaster::execute()"); - return; - } - - mpFbo->attachDepthStencilTarget(pRenderData->getTexture("depthStencil")); - - for (int i = 0; i < kGBufferChannelDesc.size(); ++i) - { - mpFbo->attachColorTarget(pRenderData->getTexture(kGBufferChannelDesc[i].name), i); - } - - pContext->clearFbo(mpFbo.get(), vec4(0), 1.f, 0, FboAttachmentType::All); - mRaster.pState->setFbo(mpFbo); - - pContext->setGraphicsState(mRaster.pState); - pContext->setGraphicsVars(mRaster.pVars); - mpSceneRenderer->renderScene(pContext); -} diff --git a/Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.cpp b/Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.cpp deleted file mode 100644 index 3708eb72d..000000000 --- a/Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "GGXGlobalIllumination.h" - -// Some global vars, used to simplify changing shader locations -namespace { - // Shader files - const char* kFileRayGen = "GGXGIRayGen.slang"; - const char* kFileRayTrace = "GGXGIIndirectRay.slang"; - const char* kFileShadowRay = "GGXGIShadowRay.slang"; - - // Entry-point names - const char* kEntryPointRayGen = "GGXGlobalIllumRayGen"; - const char* kEntryShadowMiss = "ShadowMiss"; - const char* kEntryShadowAnyHit = "ShadowAnyHit"; - - const char* kEntryIndirectMiss = "IndirectMiss"; - const char* kEntryIndirectAnyHit = "IndirectAnyHit"; - const char* kEntryIndirectClosestHit = "IndirectClosestHit"; -}; - -GGXGlobalIllumination::SharedPtr GGXGlobalIllumination::create(const Dictionary ¶ms) -{ - GGXGlobalIllumination::SharedPtr pPass(new GGXGlobalIllumination()); - - // Load parameters from Python - if (params.keyExists("useEmissives")) pPass->mUseEmissiveGeom = params["useEmissives"]; - if (params.keyExists("doDirectLight")) pPass->mDoDirectGI = params["doDirectLight"]; - if (params.keyExists("doIndirectLight")) pPass->mDoIndirectGI = params["doIndirectLight"]; - if (params.keyExists("rayDepth")) pPass->mUserSpecifiedRayDepth = params["rayDepth"]; - if (params.keyExists("randomSeed")) pPass->mFrameCount = params["randomSeed"]; - if (params.keyExists("useBlackEnvMap")) pPass->mEnvMapMode = params["useBlackEnvMap"] ? EnvMapMode::Black : EnvMapMode::Scene; - - return pPass; -} - -Dictionary GGXGlobalIllumination::getScriptingDictionary() const -{ - Dictionary serialize; - serialize["useEmissives"] = mUseEmissiveGeom; - serialize["doDirectLight"] = mDoDirectGI; - serialize["doIndirectLight"] = mDoIndirectGI; - serialize["rayDepth"] = mUserSpecifiedRayDepth; - serialize["randomSeed"] = mFrameCount; - serialize["useBlackEnvMap"] = mEnvMapMode == EnvMapMode::Black; - return serialize; -} - -RenderPassReflection GGXGlobalIllumination::reflect(void) const -{ - RenderPassReflection r; - r.addInput("posW", ""); - r.addInput("normW", ""); - r.addInput("diffuseOpacity", ""); - r.addInput("specRough", ""); - r.addInput("emissive", ""); - r.addInput("matlExtra", ""); - - r.addOutput("output", "").format(ResourceFormat::RGBA32Float).bindFlags(Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); - return r; -} - -void GGXGlobalIllumination::initialize(RenderContext* pContext, const RenderData* pRenderData) -{ - mpBlackHDR = Texture::create2D(128, 128, ResourceFormat::RGBA32Float, 1u, 1u, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget); - pContext->clearRtv(mpBlackHDR->getRTV().get(), vec4(0.0f, 0.0f, 0.0f, 1.0f)); - - mpState = RtState::create(); - mpState->setMaxTraceRecursionDepth(mMaxPossibleRayDepth); - - RtProgram::Desc desc; - desc.addShaderLibrary(kFileRayGen); - desc.setRayGen(kEntryPointRayGen); - - // Add ray type #0 (shadow rays) - desc.addShaderLibrary(kFileShadowRay); - desc.addMiss(0, kEntryShadowMiss); - desc.addHitGroup(0, "", kEntryShadowAnyHit); - - // Add ray type #1 (indirect GI rays) - desc.addShaderLibrary(kFileRayTrace); - desc.addMiss(1, kEntryIndirectMiss); - desc.addHitGroup(1, kEntryIndirectClosestHit, kEntryIndirectAnyHit); - - // Now that we've passed all our shaders in, compile and (if available) setup the scene - mpProgram = RtProgram::create(desc); - mpState->setProgram(mpProgram); - - if (mpProgram != nullptr && mpScene != nullptr) - { - mpSceneRenderer = RtSceneRenderer::create(mpScene); - mpVars = RtProgramVars::create(mpProgram, mpScene); - } - - mIsInitialized = true; -} - -void GGXGlobalIllumination::execute(RenderContext* pContext, const RenderData* pData) -{ - // On first execution, run some initialization - if (!mIsInitialized) - { - initialize(pContext, pData); - } - - // Get our output buffer and clear it - Texture::SharedPtr pDstTex = pData->getTexture("output"); - pContext->clearUAV(pDstTex->getUAV().get(), vec4(0.0f, 0.0f, 0.0f, 1.0f)); - - if (pDstTex == nullptr || mpScene == nullptr) return; - - // Set our variables into the global HLSL namespace - auto globalVars = mpVars->getGlobalVars(); - - ConstantBuffer::SharedPtr pCB = globalVars->getConstantBuffer("GlobalCB"); - pCB["gMinT"] = 1.0e-3f; - pCB["gFrameCount"] = mFrameCount++; - pCB["gDoIndirectGI"] = mDoIndirectGI; - pCB["gDoDirectGI"] = mDoDirectGI; - pCB["gMaxDepth"] = uint32_t(mUserSpecifiedRayDepth); - pCB["gEmitMult"] = float(mUseEmissiveGeom ? mEmissiveGeomMult : 0.0f); - - globalVars->setTexture("gPos", pData->getTexture("posW")); - globalVars->setTexture("gNorm", pData->getTexture("normW")); - globalVars->setTexture("gDiffuseMatl", pData->getTexture("diffuseOpacity")); - globalVars->setTexture("gSpecMatl", pData->getTexture("specRough")); - globalVars->setTexture("gExtraMatl", pData->getTexture("matlExtra")); - globalVars->setTexture("gEmissive", pData->getTexture("emissive")); - globalVars->setTexture("gOutput", pDstTex); - - const Texture::SharedPtr& pEnvMap = mpScene->getEnvironmentMap(); - globalVars->setTexture("gEnvMap", (mEnvMapMode == EnvMapMode::Black || pEnvMap == nullptr) ? mpBlackHDR : pEnvMap); - - // Launch our ray tracing - mpSceneRenderer->renderScene(pContext, mpVars, mpState, mRayLaunchDims); -} - -void GGXGlobalIllumination::renderUI(Gui* pGui, const char* uiGroup) -{ - bool changed = pGui->addIntVar("Max RayDepth", mUserSpecifiedRayDepth, 0, mMaxPossibleRayDepth); - changed |= pGui->addCheckBox("Compute direct illumination", mDoDirectGI); - changed |= pGui->addCheckBox("Compute global illumination", mDoIndirectGI); - - pGui->addSeparator(); - - const static Gui::RadioButtonGroup kButtons = - { - {(uint32_t)EnvMapMode::Scene, "Scene", false}, - {(uint32_t)EnvMapMode::Black, "Black", true} - }; - - pGui->addText("Environment Map"); - changed |= pGui->addRadioButtons(kButtons, (uint32_t&)mEnvMapMode); - - if (changed) mPassChangedCB(); -} - -void GGXGlobalIllumination::setScene(const std::shared_ptr& pScene) -{ - // Stash a copy of the scene - mpScene = std::dynamic_pointer_cast(pScene); - if (!mpScene) return; - - mpSceneRenderer = RtSceneRenderer::create(mpScene); - if(mpProgram != nullptr) mpVars = RtProgramVars::create(mpProgram, mpScene); -} - -void GGXGlobalIllumination::onResize(uint32_t width, uint32_t height) -{ - mRayLaunchDims = uvec3(width, height, 1); -} diff --git a/Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.h b/Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.h deleted file mode 100644 index d48efc72f..000000000 --- a/Samples/Raytracing/PathTracer/RenderPasses/GGXGlobalIllumination.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include "Falcor.h" -#include "FalcorExperimental.h" - -using namespace Falcor; - -/** This pass defines the multi-bounce global illumination pass in Chris' "Introduction to Direct Raytracing" - tutorials -*/ -class GGXGlobalIllumination : public RenderPass, inherit_shared_from_this -{ -public: - using SharedPtr = std::shared_ptr; - - /** Instantiate our pass. The input Python dictionary is where you can extract pass parameters - */ - static SharedPtr create(const Dictionary& params = {}); - - /** Get a string describing what the pass is doing - */ - virtual std::string getDesc() override { return "Path traces from a G-buffer to accumulate indirect light using a GGX lighting model."; } - - /** Defines the inputs/outputs required for this render pass - */ - virtual RenderPassReflection reflect(void) const override; - - /** Run our multibounce GI pass - */ - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; - - /** Display a GUI exposing rendering parameters - */ - virtual void renderUI(Gui* pGui, const char* uiGroup) override; - - /** Grab the current scene so we can render it! - */ - virtual void setScene(const std::shared_ptr& pScene) override; - - /** Do any updates needed when we resize our window - */ - virtual void onResize(uint32_t width, uint32_t height) override; - - /** Serialize the render pass parameters out to a python dictionary - */ - virtual Dictionary getScriptingDictionary() const override; - -private: - GGXGlobalIllumination() : RenderPass("GGXGlobalIllumination") {} - - /** Runs on first execute() to initialize rendering resources - */ - void initialize(RenderContext* pContext, const RenderData* pRenderData); - - // Internal pass state - RtProgram::SharedPtr mpProgram; - RtState::SharedPtr mpState; - RtProgramVars::SharedPtr mpVars; - - RtScene::SharedPtr mpScene; - RtSceneRenderer::SharedPtr mpSceneRenderer; - - // Recursive ray tracing can be slow. Add a toggle to disable, to allow you to manipulate the scene - bool mDoIndirectGI = true; - bool mDoDirectGI = true; - bool mUseEmissiveGeom = true; - float mEmissiveGeomMult = 1.0f; - Texture::SharedPtr mpBlackHDR = nullptr; - - enum class EnvMapMode : uint32_t - { - Scene, - Black - }; - - EnvMapMode mEnvMapMode = EnvMapMode::Scene; - - int32_t mUserSpecifiedRayDepth = 2; ///< What is the current maximum ray depth - const int32_t mMaxPossibleRayDepth = 4; ///< The largest ray depth we support (without recompile) - - // A frame counter; needs to start at a different value than passes, since it uses to seed the RNG - uint32_t mFrameCount = 0x1234u; ///< A frame counter to vary random numbers over time - - // Some common pass bookkeeping - uvec3 mRayLaunchDims = uvec3(0, 0, 0); - bool mIsInitialized = false; -}; diff --git a/Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.cpp b/Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.cpp deleted file mode 100644 index ccfa1f1c6..000000000 --- a/Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "TemporalAccumulation.h" - -namespace { - // Where is our shader located? - const char *kAccumShader = "Accumulate.slang"; - - // Key used to look up dirty state in the graph's shared dictionary - const char *kDirtyFlag = "_dirty"; -}; - -TemporalAccumulation::SharedPtr TemporalAccumulation::create(const Dictionary ¶ms) -{ - TemporalAccumulation::SharedPtr ptr(new TemporalAccumulation()); - - // Load parameters from Python - if (params.keyExists("doAccumulation")) ptr->mDoAccumulation = params["doAccumulation"]; - - return ptr; -} - -Dictionary TemporalAccumulation::getScriptingDictionary() const -{ - Dictionary serialize; - serialize["doAccumulation"] = mDoAccumulation; - return serialize; -} - -RenderPassReflection TemporalAccumulation::reflect(void) const -{ - RenderPassReflection r; - r.addInput("input", ""); - - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget; - r.addInternal("accum", "").format(ResourceFormat::RGBA32Float).bindFlags(bindFlags).flags(RenderPassReflection::Field::Flags::Persistent); - r.addOutput("output", "").format(ResourceFormat::RGBA32Float).bindFlags(bindFlags); - return r; -} - -void TemporalAccumulation::initialize(const Dictionary& dict) -{ - mDict = dict; - mpState = GraphicsState::create(); - mpPass = FullScreenPass::create(kAccumShader); - mpVars = GraphicsVars::create(mpPass->getProgram()->getReflector()); - - mIsInitialized = true; -} - -void TemporalAccumulation::execute(RenderContext* pContext, const RenderData* pRenderData) -{ - // On first execution, run some initialization - if (!mIsInitialized) initialize(pRenderData->getDictionary()); - - // Get references to our input, output, and temporary accumulation texture - Texture::SharedPtr pSrcTex = pRenderData->getTexture("input"); - Texture::SharedPtr pAccumTex = pRenderData->getTexture("accum"); - Texture::SharedPtr pDstTex = pRenderData->getTexture("output"); - - Fbo::SharedPtr pDstFbo = Fbo::create(); - pDstFbo->attachColorTarget(pDstTex, 0); - - // If we don't have our input our output texture, give up.... Don't know what to do... - if (!pSrcTex || !pDstTex) return; - - // We're not doing accumulation, copy the input to the output - if (!mDoAccumulation) - { - pContext->blit(pSrcTex->getSRV(), pDstTex->getRTV()); - return; - } - - // If the camera in our current scene has moved, or the GUI pass settings changed, we want to reset accumulation - auto& pDict = pRenderData->getDictionary(); - bool giDirty = pDict.keyExists(kDirtyFlag) && bool(pDict[kDirtyFlag]); - if (hasCameraMoved() || giDirty) - { - mAccumCount = 0; - mpLastCameraMatrix = mpScene->getActiveCamera()->getViewMatrix(); - - if (giDirty) pDict[kDirtyFlag] = false; // Reset the flag - } - - // Set shader parameters for our accumulation pass - mpVars["PerFrameCB"]["gAccumCount"] = mAccumCount++; // Current count of accumulated samples - mpVars->setTexture("gAccumBuf", pAccumTex); // Intermediate texture where running tally is accumulated - mpVars->setTexture("gCurFrame", pSrcTex); // Input samples for this frame - - mpState->setFbo(pDstFbo); - pContext->pushGraphicsState(mpState); - pContext->pushGraphicsVars(mpVars); - mpPass->execute(pContext); - pContext->popGraphicsVars(); - pContext->popGraphicsState(); -} - -void TemporalAccumulation::renderUI(Gui* pGui, const char* uiGroup) -{ - // Reset accumulation if settings changed, or manual reset requested - bool reset = pGui->addCheckBox("Accumulate Samples", mDoAccumulation); - reset |= pGui->addButton("Reset", true); - - if (reset) mAccumCount = 0; - - // Display amount of accumulated frames - pGui->addText((std::string("Frames accumulated: ") + std::to_string(mAccumCount)).c_str()); -} - -bool TemporalAccumulation::hasCameraMoved() -{ - // Has our camera moved? - return mpScene && // No scene? Then the answer is no - mpScene->getActiveCamera() && // No camera in our scene? Then the answer is no - (mpLastCameraMatrix != mpScene->getActiveCamera()->getViewMatrix()); // Compare the current matrix with the last one -} - -void TemporalAccumulation::setScene(const std::shared_ptr& pScene) -{ - // Reset accumulation on loading a new scene - mAccumCount = 0; - - // When our renderer moves around we want to reset accumulation, so stash the scene pointer - mpScene = pScene; - - // Grab a copy of the current scene's camera matrix (if it exists) - if (mpScene && mpScene->getActiveCamera()) - mpLastCameraMatrix = mpScene->getActiveCamera()->getViewMatrix(); -} - -void TemporalAccumulation::onResize(uint32_t width, uint32_t height) -{ - // Need to restart accumulation when we resize - mAccumCount = 0; -} diff --git a/Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.h b/Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.h deleted file mode 100644 index d111446d1..000000000 --- a/Samples/Raytracing/PathTracer/RenderPasses/TemporalAccumulation.h +++ /dev/null @@ -1,106 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" -#include "FalcorExperimental.h" - -using namespace Falcor; - -/** This render pass takes an input buffer, accumulates/averages that input (over multiple frames) - into an internal buffer, and writes the (current) averaged accumulation buffer to the output. - Accumulation is reset whenever the camera moves or when the other passes mark the dirty bit "_isDirty" - in the shared Python dictionary -*/ -class TemporalAccumulation : public RenderPass, inherit_shared_from_this -{ -public: - using SharedPtr = std::shared_ptr; - - /** Get a string describing what the pass is doing - */ - virtual std::string getDesc() override { return "Accumulates its input buffer over time."; } - - /** Instantiate our pass. The input Python dictionary is where you can extract pass parameters - */ - static SharedPtr create(const Dictionary& params = {}); - - /** Defines the inputs/outputs required for this render pass - */ - virtual RenderPassReflection reflect(void) const override; - - /** Run our accumulation buffering - */ - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; - - /** Display a GUI allowing disabling accumulation and printing a running tally of frames accumulated - */ - virtual void renderUI(Gui* pGui, const char* uiGroup) override; - - /** Grab the current scene so we can determine when the camera moves - */ - virtual void setScene(const std::shared_ptr& pScene) override; - - /** Check when the screen resizes, since we need to restart accumulation then, too - */ - virtual void onResize(uint32_t width, uint32_t height) override; - - /** Serialize the render pass parameters out to a python dictionary - */ - virtual Dictionary getScriptingDictionary() const override; - -private: - TemporalAccumulation() : RenderPass("TemporalAccumulation") {} - - void initialize(const Dictionary& dict); - - // A helper utility to determine if the current scene (if any) has had any camera motion - bool hasCameraMoved(); - - // State for our accumulation shader - GraphicsVars::SharedPtr mpVars; - GraphicsProgram::SharedPtr mpProgram; - GraphicsState::SharedPtr mpState; - FullScreenPass::UniquePtr mpPass; - - Fbo::SharedPtr mpInternalFbo; - - // We stash a copy of our current scene. Why? To detect if changes have occurred. - Scene::SharedPtr mpScene; - mat4 mpLastCameraMatrix; - - // Is our accumulation enabled? - bool mDoAccumulation = true; - - // How many frames have we accumulated so far? - uint32_t mAccumCount = 0; - - // Some common pass bookkeeping - bool mIsInitialized = false; - bool mDirtyLastFrame = false; - Dictionary mDict; ///< Our shared Python dictionary for pass communication (extracted during onInitialize()) -}; diff --git a/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.vcxproj b/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.vcxproj deleted file mode 100644 index 9cd5c3a9c..000000000 --- a/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.vcxproj +++ /dev/null @@ -1,135 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - 15.0 - {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71} - RenderGraphEditor - 10.0.17763.0 - - - - Application - true - v141 - MultiByte - - - Application - false - v141 - true - MultiByte - - - Application - true - v141 - MultiByte - - - Application - false - v141 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - - - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - \ No newline at end of file diff --git a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.cpp b/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.cpp deleted file mode 100644 index 29ca5e1c1..000000000 --- a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.cpp +++ /dev/null @@ -1,581 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "RenderGraphViewer.h" -#include - -size_t RenderGraphViewer::DebugWindow::index = 0; -namespace fs = std::experimental::filesystem; - -const std::string gkDefaultScene = "Arcade/Arcade.fscene"; -const char* kEditorExecutableName = "RenderGraphEditor"; - -const char* kSceneSwitch = "scene"; -const char* kImageSwitch = "image"; -const char* kGraphFileSwitch = "graphFile"; -const char* kGraphNameSwitch = "graphname"; -const char* kEditorSwitch = "editor"; -const char* kOutfileDirSwitch = "outputdir"; - -void RenderGraphViewer::onShutdown(SampleCallbacks* pCallbacks) -{ - resetEditor(); - mGraphs.clear(); -} - -void RenderGraphViewer::onLoad(SampleCallbacks* pCallbacks, RenderContext* pRenderContext) -{ - // if editor opened from running render graph, get the name of the file to read - mDefaultSceneName = gkDefaultScene; - parseArguments(pCallbacks, pCallbacks->getArgList()); - - const auto& pFbo = pCallbacks->getCurrentFbo(); - uint32_t w = pFbo->getWidth(); - uint32_t h = pFbo->getHeight(); - w = (uint32_t)(w * 0.25f); - h = (uint32_t)(h * 0.6f); - pCallbacks->setDefaultGuiSize(w, h); -} - -void RenderGraphViewer::parseArguments(SampleCallbacks* pCallbacks, const ArgList& argList) -{ - if (argList.argExists(kSceneSwitch)) - { - std::string filename = argList[kSceneSwitch].asString(); - if (filename.size()) { mDefaultSceneName = filename; } - else msgBox("No path to default scene provided."); - } - if (argList.argExists(kImageSwitch)) - { - std::string filename = argList[kImageSwitch].asString(); - if (filename.size()) { mDefaultImageName = filename; } - else msgBox("No path to default image provided."); - } - if (argList.argExists(kGraphFileSwitch)) - { - std::string filename = argList[kGraphFileSwitch].asString(); - if (filename.size()) - { - if (argList.argExists(kGraphNameSwitch)) - { - std::string graphName = argList[kGraphNameSwitch].asString(); - if (graphName.size()) - { - auto pGraph = RenderGraphImporter::import(graphName, filename); - mGraphs.push_back({}); - initGraph(pGraph, graphName, filename, pCallbacks, mGraphs.back()); - } - } - else { addGraphsFromFile(filename, pCallbacks); } - } - else { msgBox("No file path provided for input graph file"); } - - if (argList.argExists(kEditorSwitch)) - { - mEditorTempFile = filename; - monitorFileUpdates(filename, std::bind(&RenderGraphViewer::editorFileChangeCB, this)); - mEditorProcess = kInvalidProcessId; - } - } - - mOutputImageDir = argList.argExists(kOutfileDirSwitch) ? argList[kOutfileDirSwitch].asString() : getExecutableDirectory(); -} - -bool isInVector(const std::vector& strVec, const std::string& str) -{ - return std::find(strVec.begin(), strVec.end(), str) != strVec.end(); -} - -Gui::DropdownList createDropdownFromVec(const std::vector& strVec, const std::string& currentLabel) -{ - Gui::DropdownList dropdown; - for (size_t i = 0; i < strVec.size(); i++) dropdown.push_back({ (uint32_t)i, strVec[i] }); - return dropdown; -} - -void RenderGraphViewer::addDebugWindow() -{ - DebugWindow window; - window.windowName = "Debug Window " + std::to_string(DebugWindow::index++); - window.currentOutput = mGraphs[mActiveGraph].mainOutput; - markOutput(window.currentOutput); - mGraphs[mActiveGraph].debugWindows.push_back(window); -} - -void RenderGraphViewer::unmarkOutput(const std::string& name) -{ - auto& graphData = mGraphs[mActiveGraph]; - // Skip the original outputs - if (isInVector(graphData.originalOutputs, name)) return; - - // Decrease the reference counter - auto& ref = graphData.graphOutputRefs.at(name); - ref--; - if (ref == 0) - { - graphData.graphOutputRefs.erase(name); - graphData.pGraph->unmarkOutput(name); - } -} - -void RenderGraphViewer::markOutput(const std::string& name) -{ - auto& graphData = mGraphs[mActiveGraph]; - // Skip the original outputs - if (isInVector(graphData.originalOutputs, name)) return; - auto& refVec = mGraphs[mActiveGraph].graphOutputRefs; - refVec[name]++; - if (refVec[name] == 1) mGraphs[mActiveGraph].pGraph->markOutput(name); -} - -void RenderGraphViewer::renderOutputUI(Gui* pGui, const Gui::DropdownList& dropdown, std::string& selectedOutput) -{ - uint32_t activeOut = -1; - for(size_t i = 0 ; i < dropdown.size() ; i++) - { - if (dropdown[i].label == selectedOutput) - { - activeOut = (uint32_t)i; - break; - } - } - - // This can happen when `showAllOutputs` changes to false, and the chosen output is not an original output. We will force an output change - bool forceOutputChange = activeOut == -1; - if (forceOutputChange) activeOut = 0; - - if (pGui->addDropdown("Output", dropdown, activeOut) || forceOutputChange) - { - // Unmark old output, set new output, mark new output - unmarkOutput(selectedOutput); - selectedOutput = dropdown[activeOut].label; - markOutput(selectedOutput); - } -} - -bool RenderGraphViewer::renderDebugWindow(Gui* pGui, const Gui::DropdownList& dropdown, DebugWindow& data, const uvec2& winSize) -{ - // Get the current output, in case `renderOutputUI()` unmarks it - Texture::SharedPtr pTex = std::dynamic_pointer_cast(mGraphs[mActiveGraph].pGraph->getOutput(data.currentOutput)); - std::string label = data.currentOutput + "##" + mGraphs[mActiveGraph].name; - if (!pTex) { logError("Invalid output resource. Is not a texture."); } - - uvec2 debugSize = (uvec2)(vec2(winSize) * vec2(0.4f, 0.55f)); - uvec2 debugPos = winSize - debugSize; - debugPos -= 10; - - // Display the dropdown - pGui->pushWindow(data.windowName.c_str(), debugSize.x, debugSize.y, debugPos.x, debugPos.y); - bool close = pGui->addButton("Close"); - if (pGui->addButton("Save To File", true)) Bitmap::saveImageDialog(pTex); - renderOutputUI(pGui, dropdown, data.currentOutput); - pGui->addSeparator(); - - // Display the image - pGui->addImage(label.c_str(), pTex); - - pGui->popWindow(); - - return close; -} - -void RenderGraphViewer::eraseDebugWindow(size_t id) -{ - unmarkOutput(mGraphs[mActiveGraph].debugWindows[id].currentOutput); - mGraphs[mActiveGraph].debugWindows.erase(mGraphs[mActiveGraph].debugWindows.begin() + id); -} - -void RenderGraphViewer::graphOutputsGui(Gui* pGui, SampleCallbacks* pCallbacks) -{ - RenderGraph::SharedPtr pGraph = mGraphs[mActiveGraph].pGraph; - if (mGraphs[mActiveGraph].debugWindows.size()) mGraphs[mActiveGraph].showAllOutputs = true; - auto strVec = mGraphs[mActiveGraph].showAllOutputs ? pGraph->getAvailableOutputs() : mGraphs[mActiveGraph].originalOutputs; - Gui::DropdownList graphOuts = createDropdownFromVec(strVec, mGraphs[mActiveGraph].mainOutput); - - pGui->addCheckBox("List All Outputs", mGraphs[mActiveGraph].showAllOutputs); - pGui->addTooltip("Display every possible output in the render-graph, even if it wasn't explicitly marked as one. If there's a debug window open, you won't be able to uncheck this"); - - if (graphOuts.size()) - { - uvec2 dims(pCallbacks->getCurrentFbo()->getWidth(), pCallbacks->getCurrentFbo()->getHeight()); - - renderOutputUI(pGui, graphOuts, mGraphs[mActiveGraph].mainOutput); - for (size_t i = 0; i < mGraphs[mActiveGraph].debugWindows.size();) - { - if (renderDebugWindow(pGui, graphOuts, mGraphs[mActiveGraph].debugWindows[i], dims)) - { - eraseDebugWindow(i); - } - else i++; - } - - // Render the debug windows *before* adding/removing debug windows - if (pGui->addButton("Show In Debug Window")) addDebugWindow(); - if(mGraphs[mActiveGraph].debugWindows.size()) - { - if (pGui->addButton("Close all debug windows")) - { - while (mGraphs[mActiveGraph].debugWindows.size()) eraseDebugWindow(0); - } - } - } -} - -void RenderGraphViewer::onGuiRender(SampleCallbacks* pCallbacks, Gui* pGui) -{ - if (pGui->addButton("Add Graphs")) addGraphDialog(pCallbacks); - if (pGui->addButton("Load Scene", true)) loadScene(pCallbacks); - pGui->addSeparator(); - - // Display a list with all the graphs - if (mGraphs.size()) - { - if(pGui->addButton("Reload libraries")) - { - RenderPassLibrary::instance().reloadLibraries(); - } - - Gui::DropdownList graphList; - for (size_t i = 0; i < mGraphs.size(); i++) graphList.push_back({ (uint32_t)i, mGraphs[i].pGraph->getName() }); - if(mEditorProcess == 0) - { - pGui->addDropdown("Active Graph", graphList, mActiveGraph); - if (pGui->addButton("Edit")) openEditor(); - if (pGui->addButton("Remove", true)) - { - removeActiveGraph(); - if (mGraphs.empty()) return; - } - pGui->addSeparator(); - } - - // Active graph output - graphOutputsGui(pGui, pCallbacks); - - // Graph UI - pGui->addSeparator(); - mGraphs[mActiveGraph].pGraph->renderUI(pGui, mGraphs[mActiveGraph].pGraph->getName().c_str()); - } -} - -void RenderGraphViewer::onDroppedFile(SampleCallbacks* pCallbacks, const std::string& filename) -{ - std::string ext = getExtensionFromFile(filename); - if (ext == "fscene") loadSceneFromFile(filename, pCallbacks); - else if (ext == "py") addGraphsFromFile(filename, pCallbacks); - logWarning("RenderGraphViewer::onDroppedFile() - Unknown file extension `" + ext + "`"); -} - -void RenderGraphViewer::editorFileChangeCB() -{ - mEditorScript = readFile(mEditorTempFile); -} - -void RenderGraphViewer::openEditor() -{ - bool unmarkOut = (isInVector(mGraphs[mActiveGraph].originalOutputs, mGraphs[mActiveGraph].mainOutput) == false); - // If the current graph output is not an original output, unmark it - if (unmarkOut) mGraphs[mActiveGraph].pGraph->unmarkOutput(mGraphs[mActiveGraph].mainOutput); - - mEditorTempFile = getTempFilename(); - - // Save the graph - RenderGraphExporter::save(mGraphs[mActiveGraph].pGraph, mGraphs[mActiveGraph].name, mEditorTempFile); - - // Register an update callback - monitorFileUpdates(mEditorTempFile, std::bind(&RenderGraphViewer::editorFileChangeCB, this)); - - // Run the process - std::string commandLineArgs = '-' + std::string(kEditorSwitch) + " -" + std::string(kGraphFileSwitch); - commandLineArgs += ' ' + mEditorTempFile + " -" + std::string(kGraphNameSwitch) + ' ' + mGraphs[mActiveGraph].name; - mEditorProcess = executeProcess(kEditorExecutableName, commandLineArgs); - - // Mark the output if it's required - if (unmarkOut) mGraphs[mActiveGraph].pGraph->markOutput(mGraphs[mActiveGraph].mainOutput); -} - -void RenderGraphViewer::resetEditor() -{ - if(mEditorProcess) - { - closeSharedFile(mEditorTempFile); - std::remove(mEditorTempFile.c_str()); - if(mEditorProcess != kInvalidProcessId) - { - terminateProcess(mEditorProcess); - mEditorProcess = 0; - } - } -} - -void RenderGraphViewer::removeActiveGraph() -{ - assert(mGraphs.size()); - mGraphs.erase(mGraphs.begin() + mActiveGraph); - mActiveGraph = 0; -} - -std::vector RenderGraphViewer::getGraphOutputs(const RenderGraph::SharedPtr& pGraph) -{ - std::vector outputs; - for (size_t i = 0; i < pGraph->getOutputCount(); i++) outputs.push_back(pGraph->getOutputName(i)); - return outputs; -} - -void RenderGraphViewer::initGraph(const RenderGraph::SharedPtr& pGraph, const std::string& name, const std::string& filename, SampleCallbacks* pCallbacks, GraphData& data) -{ - if (pGraph->getName().empty()) pGraph->setName(name); - - // Set input image if it exists - if(mDefaultImageName.size()) (*pGraph->getPassesDictionary())[kImageSwitch] = mDefaultImageName; - - data.name = name; - data.filename = filename; - data.fileModifiedTime = getFileModifiedTime(filename); - data.pGraph = pGraph; - if(data.pGraph->getScene() == nullptr) - { - if (!mpDefaultScene) loadSceneFromFile(mDefaultSceneName, pCallbacks); - data.pGraph->setScene(mpDefaultScene); - } - if (data.pGraph->getOutputCount() != 0) data.mainOutput = data.pGraph->getOutputName(0); - - // Store the original outputs - data.originalOutputs = getGraphOutputs(pGraph); -} - -void RenderGraphViewer::addGraphDialog(SampleCallbacks* pCallbacks) -{ - std::string filename; - if (openFileDialog(RenderGraph::kFileExtensionFilters, filename)) addGraphsFromFile(filename, pCallbacks); -} - -void RenderGraphViewer::addGraphsFromFile(const std::string& filename, SampleCallbacks* pCallbacks) -{ - const auto& pTargetFbo = pCallbacks->getCurrentFbo().get(); - auto graphs = RenderGraphImporter::importAllGraphs(filename, pTargetFbo); - - for (auto& newG : graphs) - { - bool found = false; - // Check if the graph already exists. If it is, replace it - for (auto& oldG : mGraphs) - { - if (oldG.name == newG.name) - { - found = true; - logWarning("Graph `" + newG.name + "` already exists. Replacing it"); - initGraph(newG.pGraph, newG.name, filename, pCallbacks, oldG); - } - } - - if (!found) - { - mGraphs.push_back({}); - initGraph(newG.pGraph, newG.name, filename, pCallbacks, mGraphs.back()); - } - } -} - -void RenderGraphViewer::loadScene(SampleCallbacks* pCallbacks) -{ - std::string filename; - if (openFileDialog(Scene::kFileExtensionFilters, filename)) - { - loadSceneFromFile(filename, pCallbacks); - } -} - -void RenderGraphViewer::loadSceneFromFile(const std::string& filename, SampleCallbacks* pCallbacks) -{ -#ifdef FALCOR_D3D12 - mpDefaultScene = gpDevice->isFeatureSupported(Device::SupportedFeatures::Raytracing) ? RtScene::loadFromFile(filename) : Scene::loadFromFile(filename); -#else - mpDefaultScene = Scene::loadFromFile(filename); -#endif - const auto& pFbo = pCallbacks->getCurrentFbo(); - float ratio = float(pFbo->getWidth()) / float(pFbo->getHeight()); - mpDefaultScene->setCamerasAspectRatio(ratio); - for(auto& g : mGraphs) g.pGraph->setScene(mpDefaultScene); -} - -void RenderGraphViewer::applyEditorChanges() -{ - if (!mEditorProcess) return; - // If the editor was closed, reset the handles - if ((mEditorProcess != kInvalidProcessId) && isProcessRunning(mEditorProcess) == false) resetEditor(); - - if (mEditorScript.empty()) return; - - // Unmark the current output if it wasn't an original one - bool unmarkOut = (isInVector(mGraphs[mActiveGraph].originalOutputs, mGraphs[mActiveGraph].mainOutput) == false); - if (unmarkOut) mGraphs[mActiveGraph].pGraph->unmarkOutput(mGraphs[mActiveGraph].mainOutput); - - // Run the scripting - auto pScripting = RenderGraphScripting::create(); - pScripting->addGraph(mGraphs[mActiveGraph].name, mGraphs[mActiveGraph].pGraph); - pScripting->runScript(mEditorScript); - - // Update the original output list - mGraphs[mActiveGraph].originalOutputs = getGraphOutputs(mGraphs[mActiveGraph].pGraph); - - // Mark the current output if it's required - if (unmarkOut) mGraphs[mActiveGraph].pGraph->markOutput(mGraphs[mActiveGraph].mainOutput); - - mEditorScript.clear(); -} - -void RenderGraphViewer::onFrameRender(SampleCallbacks* pCallbacks, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - applyEditorChanges(); - - // Render - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - if (mGraphs.size()) - { - auto& pGraph = mGraphs[mActiveGraph].pGraph; - if (pGraph->getScene()) pGraph->getScene()->update(pCallbacks->getCurrentTime(), &mCamController); - - pGraph->execute(pRenderContext); - if(mGraphs[mActiveGraph].mainOutput.size()) - { - Texture::SharedPtr pOutTex = std::dynamic_pointer_cast(pGraph->getOutput(mGraphs[mActiveGraph].mainOutput)); - pRenderContext->blit(pOutTex->getSRV(), pTargetFbo->getRenderTargetView(0)); - } - } -} - -bool RenderGraphViewer::onMouseEvent(SampleCallbacks* pCallbacks, const MouseEvent& mouseEvent) -{ - if (mGraphs.size()) mGraphs[mActiveGraph].pGraph->onMouseEvent(mouseEvent); - return mCamController.onMouseEvent(mouseEvent); -} - -bool RenderGraphViewer::onKeyEvent(SampleCallbacks* pCallbacks, const KeyboardEvent& keyEvent) -{ - if (mGraphs.size()) mGraphs[mActiveGraph].pGraph->onKeyEvent(keyEvent); - return mCamController.onKeyEvent(keyEvent); -} - -void RenderGraphViewer::onResizeSwapChain(SampleCallbacks* pCallbacks, uint32_t width, uint32_t height) -{ - for(auto& g : mGraphs) - { - g.pGraph->onResize(pCallbacks->getCurrentFbo().get()); - g.pGraph->getScene()->setCamerasAspectRatio((float)width / (float)height); - } - if (mpDefaultScene) mpDefaultScene->setCamerasAspectRatio((float)width / (float)height); -} - -// testing -void RenderGraphViewer::onInitializeTesting(SampleCallbacks* pCallbacks) -{ - auto args = pCallbacks->getArgList(); - std::vector scene = args.getValues("loadscene"); - if (!scene.empty()) - { - loadSceneFromFile(scene[0].asString(), pCallbacks); - } - - std::vector cameraPos = args.getValues("camerapos"); - if (!cameraPos.empty()) - { - mpDefaultScene->getActiveCamera()->setPosition(glm::vec3(cameraPos[0].asFloat(), cameraPos[1].asFloat(), cameraPos[2].asFloat())); - } - - std::vector cameraTarget = args.getValues("cameratarget"); - if (!cameraTarget.empty()) - { - mpDefaultScene->getActiveCamera()->setTarget(glm::vec3(cameraTarget[0].asFloat(), cameraTarget[1].asFloat(), cameraTarget[2].asFloat())); - } -} - -void RenderGraphViewer::onTestFrame(SampleCallbacks* pCallbacks) -{ - for (const std::string& outputName : mGraphs[mActiveGraph].originalOutputs) - { - std::string sceneName; - std::string imageName; - - if (mDefaultSceneName.size()) sceneName = fs::path(getFilenameFromPath(mDefaultSceneName)).stem().string() + '_'; - if (mDefaultImageName.size()) imageName = fs::path(getFilenameFromPath(mDefaultImageName)).stem().string() + '_'; - - std::string filename = mGraphs[mActiveGraph].name + '_' + sceneName + imageName + outputName + '.' + std::to_string(pCallbacks->getFrameID()); - Texture::SharedPtr pTexture = std::dynamic_pointer_cast(mGraphs[mActiveGraph].pGraph->getOutput(outputName)); - if (!pTexture) logError("Invalid output resource. Is not a texture."); - - // get extension from texture resource format - std::string format = Bitmap::getFilExtFromResourceFormat(pTexture->getFormat()); - std::string outputFilePath = mOutputImageDir + '/' + filename; - outputFilePath = replaceSubstring(outputFilePath, "\\", "/"); - pTexture->captureToFile(0, 0, outputFilePath + '.' + format, Bitmap::getFormatFromFileExtension(format)); - } -} - -void RenderGraphViewer::onDataReload(SampleCallbacks* pCallbacks) -{ - if (mEditorProcess) - { - logWarning("Warning: Updating graph while editor is open. Graphs will not be reloaded from file."); - return; - } - - // Reload all DLLs - RenderPassLibrary::instance().reloadLibraries(); - - // Reload all graphs, while maintaining state - for (const auto& g : mGraphs) - { - if(g.fileModifiedTime != getFileModifiedTime(g.filename)) - { - RenderGraph::SharedPtr pGraph = g.pGraph; - RenderGraph::SharedPtr pNewGraph; - pNewGraph = RenderGraphImporter::import(g.name, g.filename); - if(pNewGraph) pGraph->update(pNewGraph); - } - } -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - RenderGraphViewer::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Render Graph Viewer"; - config.windowDesc.resizableWindow = true; -#ifndef _WIN32 - config.argc = argc; - config.argv = argv; -#endif - Sample::run(config, pRenderer); - return 0; -} diff --git a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.h b/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.h deleted file mode 100644 index 43af095f7..000000000 --- a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.h +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" -#include "FalcorExperimental.h" - -using namespace Falcor; - -class RenderGraphViewer : public Renderer -{ -public: - void onLoad(SampleCallbacks* pCallbacks, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pCallbacks, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pCallbacks, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pCallbacks, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pCallbacks, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pCallbacks, Gui* pGui) override; - void onDataReload(SampleCallbacks* pCallbacks) override; - void onShutdown(SampleCallbacks* pCallbacks) override; - void onDroppedFile(SampleCallbacks* pCallbacks, const std::string& filename) override; - - // testing - void onInitializeTesting(SampleCallbacks* pCallbacks) override; - void onTestFrame(SampleCallbacks* pCallbacks) override; - -private: - Scene::SharedPtr mpDefaultScene; - FirstPersonCameraController mCamController; - void addGraphDialog(SampleCallbacks* pCallbacks); - void addGraphsFromFile(const std::string& filename, SampleCallbacks* pCallbacks); - void removeActiveGraph(); - void loadScene(SampleCallbacks* pCallbacks); - void loadSceneFromFile(const std::string& filename, SampleCallbacks* pCallbacks); - - struct DebugWindow - { - std::string windowName; - std::string currentOutput; - static size_t index; - }; - - struct GraphData - { - time_t fileModifiedTime; - std::string filename; - std::string name; - RenderGraph::SharedPtr pGraph; - std::string mainOutput; - bool showAllOutputs = false; - std::vector originalOutputs; - std::vector debugWindows; - std::unordered_map graphOutputRefs; - }; - - void initGraph(const RenderGraph::SharedPtr& pGraph, const std::string& name, const std::string& filename, SampleCallbacks* pCallbacks, GraphData& data); - std::vector getGraphOutputs(const RenderGraph::SharedPtr& pGraph); - void parseArguments(SampleCallbacks* pCallbacks, const ArgList& argList); - void graphOutputsGui(Gui* pGui, SampleCallbacks* pCallbacks); - bool renderDebugWindow(Gui* pGui, const Gui::DropdownList& dropdown, DebugWindow& data, const uvec2& winSize); // Returns true if we need to close the window - void renderOutputUI(Gui* pGui, const Gui::DropdownList& dropdown, std::string& selectedOutput); - void addDebugWindow(); - void eraseDebugWindow(size_t id); - void unmarkOutput(const std::string& name); - void markOutput(const std::string& name); - - std::vector mGraphs; - uint32_t mActiveGraph = 0; - - // Editor stuff - void openEditor(); - void resetEditor(); - void editorFileChangeCB(); - void applyEditorChanges(); - - static const size_t kInvalidProcessId = -1; // We use this to know that the editor was launching the viewer - size_t mEditorProcess = 0; - std::string mEditorTempFile; - std::string mEditorScript; - std::string mDefaultSceneName; - std::string mDefaultImageName; - std::string mOutputImageDir; -}; diff --git a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj.filters b/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj.filters deleted file mode 100644 index 1ee61d5d6..000000000 --- a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj.filters +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - {392d281b-8330-4036-9268-e190dc6c5dc0} - - - - - Data - - - \ No newline at end of file diff --git a/Samples/RenderGraph/RenderGraphViewer/Testing/testAnimation.py b/Samples/RenderGraph/RenderGraphViewer/Testing/testAnimation.py deleted file mode 100644 index 6080bbc0b..000000000 --- a/Samples/RenderGraph/RenderGraphViewer/Testing/testAnimation.py +++ /dev/null @@ -1,20 +0,0 @@ -def render_graph_testAnimation(): - testAnimation = createRenderGraph() - DepthPass = createRenderPass("DepthPass", {'depthFormat': Format.D32Float}) - testAnimation.addPass(DepthPass, "DepthPass") - SkyBox = createRenderPass("SkyBox") - testAnimation.addPass(SkyBox, "SkyBox") - ForwardLightingPass = createRenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) - testAnimation.addPass(ForwardLightingPass, "ForwardLightingPass") - BlitPass = createRenderPass("BlitPass", {'filter': Filter.Linear}) - testAnimation.addPass(BlitPass, "BlitPass") - testAnimation.addEdge("ForwardLightingPass.color", "BlitPass.src") - testAnimation.addEdge("DepthPass.depth", "ForwardLightingPass.depth") - testAnimation.addEdge("DepthPass.depth", "SkyBox.depth") - testAnimation.addEdge("SkyBox.target", "ForwardLightingPass.color") - testAnimation.markOutput("BlitPass.dst") - Cerberus = loadScene("Cerberus/Standard/Cerberus.fscene") - testAnimation.setScene(Cerberus) - return testAnimation - -testAnimation = render_graph_testAnimation() \ No newline at end of file diff --git a/Samples/RenderGraph/RenderGraphViewer/Testing/testForwardLighting.py b/Samples/RenderGraph/RenderGraphViewer/Testing/testForwardLighting.py deleted file mode 100644 index 14a577be8..000000000 --- a/Samples/RenderGraph/RenderGraphViewer/Testing/testForwardLighting.py +++ /dev/null @@ -1,18 +0,0 @@ -def render_graph_testForwardLighting(): - testForwardLighting = createRenderGraph() - DepthPass = createRenderPass("DepthPass", {'depthFormat': Format.D32Float}) - testForwardLighting.addPass(DepthPass, "DepthPass") - SkyBox = createRenderPass("SkyBox") - testForwardLighting.addPass(SkyBox, "SkyBox") - ForwardLightingPass = createRenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) - testForwardLighting.addPass(ForwardLightingPass, "ForwardLightingPass") - BlitPass = createRenderPass("BlitPass", {'filter': Filter.Linear}) - testForwardLighting.addPass(BlitPass, "BlitPass") - testForwardLighting.addEdge("ForwardLightingPass.color", "BlitPass.src") - testForwardLighting.addEdge("DepthPass.depth", "ForwardLightingPass.depth") - testForwardLighting.addEdge("DepthPass.depth", "SkyBox.depth") - testForwardLighting.addEdge("SkyBox.target", "ForwardLightingPass.color") - testForwardLighting.markOutput("BlitPass.dst") - return testForwardLighting - -testForwardLighting = render_graph_testForwardLighting() \ No newline at end of file diff --git a/Samples/RenderGraph/RenderGraphViewer/Testing/testToneMapping.py b/Samples/RenderGraph/RenderGraphViewer/Testing/testToneMapping.py deleted file mode 100644 index 893c73181..000000000 --- a/Samples/RenderGraph/RenderGraphViewer/Testing/testToneMapping.py +++ /dev/null @@ -1,14 +0,0 @@ -def render_graph_testToneMapping(): - testToneMapping = createRenderGraph() - ImageLoader = createRenderPass("ImageLoader", {'fileName': '', 'mips': True, 'srgb': True}) - testToneMapping.addPass(ImageLoader, "ImageLoader") - ToneMapping = createRenderPass("ToneMapping", {'operator': ToneMapOp.Aces}) - testToneMapping.addPass(ToneMapping, "ToneMapping") - BlitPass = createRenderPass("BlitPass", {'filter': Filter.Linear}) - testToneMapping.addPass(BlitPass, "BlitPass") - testToneMapping.addEdge("ImageLoader.dst", "ToneMapping.src") - testToneMapping.addEdge("ToneMapping.dst", "BlitPass.src") - testToneMapping.markOutput("BlitPass.dst") - return testToneMapping - -testToneMapping = render_graph_testToneMapping() \ No newline at end of file diff --git a/Samples/RenderGraph/SamplePassLibrary/Data/forward_renderer_with_dll.py b/Samples/RenderGraph/SamplePassLibrary/Data/forward_renderer_with_dll.py deleted file mode 100644 index 61ffe07c3..000000000 --- a/Samples/RenderGraph/SamplePassLibrary/Data/forward_renderer_with_dll.py +++ /dev/null @@ -1,32 +0,0 @@ -def render_graph_forward_renderer(): - skyBox = createRenderPass("SkyBox") - - loadRenderPassLibrary("SamplePassLibrary.Dll") - - forward_renderer = createRenderGraph() - forward_renderer.addPass(createRenderPass("DepthPass"), "DepthPrePass") - forward_renderer.addPass(createRenderPass("ForwardLightingPass"), "LightingPass") - forward_renderer.addPass(createRenderPass("CascadedShadowMaps"), "ShadowPass") - forward_renderer.addPass(createRenderPass("MyBlitPass"), "MyBlitPass") - forward_renderer.addPass(createRenderPass("ToneMapping"), "ToneMapping") - forward_renderer.addPass(createRenderPass("SSAO"), "SSAO") - forward_renderer.addPass(createRenderPass("FXAA"), "FXAA") - - forward_renderer.addPass(skyBox, "SkyBox") - - forward_renderer.addEdge("DepthPrePass.depth", "SkyBox.depth"); - forward_renderer.addEdge("SkyBox.target", "LightingPass.color"); - forward_renderer.addEdge("DepthPrePass.depth", "ShadowPass.depth"); - forward_renderer.addEdge("DepthPrePass.depth", "LightingPass.depth"); - forward_renderer.addEdge("ShadowPass.visibility", "LightingPass.visibilityBuffer"); - forward_renderer.addEdge("LightingPass.color", "ToneMapping.src"); - forward_renderer.addEdge("ToneMapping.dst", "SSAO.colorIn"); - forward_renderer.addEdge("LightingPass.normals", "SSAO.normals"); - forward_renderer.addEdge("LightingPass.depth", "SSAO.depth"); - forward_renderer.addEdge("SSAO.colorOut", "FXAA.src"); - forward_renderer.addEdge("FXAA.dst", "MyBlitPass.src"); - - forward_renderer.markOutput("MyBlitPass.dst") - return forward_renderer - -forward_renderer = render_graph_forward_renderer() \ No newline at end of file diff --git a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.cpp b/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.cpp deleted file mode 100644 index dae52a34b..000000000 --- a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "SamplePassLibrary.h" - -extern "C" __declspec(dllexport) const char* getProjDir() -{ - return PROJECT_DIR; -} - -static const std::string kDst = "dst"; -static const std::string kSrc = "src"; -static const std::string kFilter = "filter"; -const char* MyBlitPass::kDesc = "Blit one texture to another"; - -RenderPassReflection MyBlitPass::reflect() const -{ - RenderPassReflection reflector; - - reflector.addOutput(kDst, "Destination texture"); - reflector.addInput(kSrc, "Source texture"); - - return reflector; -} - -static bool parseDictionary(MyBlitPass* pPass, const Dictionary& dict) -{ - for (const auto& v : dict) - { - if (v.key() == kFilter) - { - Sampler::Filter f = (Sampler::Filter)v.val(); - pPass->setFilter(f); - } - else - { - logWarning("Unknown field `" + v.key() + "` in a MyBlitPass dictionary"); - } - } - return true; -} - -MyBlitPass::SharedPtr MyBlitPass::create(const Dictionary& dict) -{ - SharedPtr pPass = SharedPtr(new MyBlitPass); - return parseDictionary(pPass.get(), dict) ? pPass : nullptr; -} - -Dictionary MyBlitPass::getScriptingDictionary() const -{ - Dictionary dict; - dict[kFilter] = mFilter; - return dict; -} - -MyBlitPass::MyBlitPass() : RenderPass("MyBlitPass") -{ -} - -void MyBlitPass::execute(RenderContext* pContext, const RenderData* pRenderData) -{ - const auto& pSrcTex = pRenderData->getTexture(kSrc); - const auto& pDstTex = pRenderData->getTexture(kDst); - - if (pSrcTex && pDstTex) - { - pContext->blit(pSrcTex->getSRV(), pDstTex->getRTV(), uvec4(-1), uvec4(-1), mFilter); - } - else - { - logWarning("MyBlitPass::execute() - missing an input or output resource"); - } -} - -void MyBlitPass::renderUI(Gui* pGui, const char* uiGroup) -{ - if (!uiGroup || pGui->beginGroup(uiGroup)) - { - static const Gui::DropdownList kFilterList = - { - { (uint32_t)Sampler::Filter::Linear, "Linear" }, - { (uint32_t)Sampler::Filter::Point, "Point" }, - }; - - uint32_t f = (uint32_t)mFilter; - if (pGui->addDropdown("Filter", kFilterList, f)) setFilter((Sampler::Filter)f); - - if (uiGroup) pGui->endGroup(); - } -} - -extern "C" __declspec(dllexport) void getPasses(RenderPassLibrary& lib) -{ - lib.registerClass("MyBlitPass", "My Blit Class", MyBlitPass::create); -} diff --git a/Samples/Utils/FalcorTest/FalcorTest.vcxproj.Filters b/Samples/Utils/FalcorTest/FalcorTest.vcxproj.Filters deleted file mode 100644 index ae38cfd84..000000000 --- a/Samples/Utils/FalcorTest/FalcorTest.vcxproj.Filters +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Tests - - - - - - - - {755f6be9-89ff-4bda-b45b-4181e83d7d0a} - - - {ce64d89a-ce01-4012-9706-d3f24f5da801} - - - - - Data - - - \ No newline at end of file diff --git a/Samples/Utils/FalcorTest/Tests/ShadingUtilsTests.cpp b/Samples/Utils/FalcorTest/Tests/ShadingUtilsTests.cpp deleted file mode 100644 index 0a9db8b46..000000000 --- a/Samples/Utils/FalcorTest/Tests/ShadingUtilsTests.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "UnitTest.h" - -namespace Falcor -{ - - // Just check the first four values. - GPU_TEST(RadicalInverse) - { - ctx.createProgram("ShadingUtilsTests.cs.slang", "testRadicalInverse"); - ctx.allocateStructuredBuffer("result", 4); - ctx["TestCB"]["resultSize"] = 4; - ctx.runProgram(); - - const float *s = ctx.mapBuffer("result"); - EXPECT_EQ(s[0], 0.f); - EXPECT_EQ(s[1], 0.5f); - EXPECT_EQ(s[2], 0.25f); - EXPECT_EQ(s[3], 0.75f); - ctx.unmapBuffer("result"); - } - - GPU_TEST(Random) - { - ctx.createProgram("ShadingUtilsTests.cs.slang", "testRand"); - const int32_t n = 4 * 1024 * 1024; - ctx.allocateStructuredBuffer("result", n); - ctx["TestCB"]["resultSize"] = n; - ctx.runProgram(); - - // A fairly crude test: bucket the range [0,1] into nBuckets buckets - // and make sure that all of them have more or less 1/nBuckets of the - // total values. This doesn't really test the quality of the PRNG very - // well, but will at least detect if it's totally borked. - const float* r = ctx.mapBuffer("result"); - constexpr int32_t nBuckets = 64; - int32_t counts[nBuckets] = { 0 }; - for (int32_t i = 0; i < n; ++i) - { - EXPECT(r[i] >= 0 && r[i] < 1.f) << r[i]; - ++counts[int32_t(r[i] * nBuckets)]; - } - ctx.unmapBuffer("result"); - - for (int32_t i = 0; i < nBuckets; ++i) - { - EXPECT_GT(counts[i], .98 * n / nBuckets); - EXPECT_LT(counts[i], 1.02 * n / nBuckets); - } - } - - GPU_TEST(SphericalCoordinates) - { - ctx.createProgram("ShadingUtilsTests.cs.slang", "testSphericalCoordinates"); - constexpr int32_t n = 1024 * 1024; - ctx.allocateStructuredBuffer("result", n); - ctx["TestCB"]["resultSize"] = n; - // The shader runs threadgroups of 1024 threads. - ctx.runProgram(n); - - // The shader generates a bunch of random vectors, converts them to - // spherical coordinates and back, and computes the dot product with - // the original vector. Here, we'll check that the dot product is - // pretty close to one. - const float* r = ctx.mapBuffer("result"); - for (int32_t i = 0; i < n; ++i) - { - EXPECT_GT(r[i], .999f) << "i = " << i; - EXPECT_LT(r[i], 1.001f) << "i = " << i; - } - ctx.unmapBuffer("result"); - } - -} // namespace Falcor diff --git a/Samples/Utils/LightProbeViewer/Data/UnitSphere.fbx b/Samples/Utils/LightProbeViewer/Data/UnitSphere.fbx deleted file mode 100644 index d0e963fcce5b7f4e3e6f1070fd3acc790ba98813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32736 zcmdUY30zZ0*SEg5Qj3sUwbYHcRcft9MZtv-Eh=@5ii(INB1EJngq;vb($-p3)U;Yf z1d^&%Tv$|Ua3P6`iWVVmY@$R!2qZ{=5FjDTHzx@iTeqjr@ArN0cQuFH`JZ#<%$YN1 z=FYub7W>Br`iJ|ET=31>k>3P|_=m@i9NBAVub+nZ>czC__2T#x;onmH11aJ28IhEb zh+ulidIa`HpAXUJlNazBN;rTE6@7a3Vpwr}j5vBQP=3$~v!5Rl5FQ&EiR4zrhEf(H zsMimm+Hwr=2&ed)&|n(HE7G49DnUq3-8MaH8&H*XR=@{76#wvrHK-LjtI`jb(nBIQ zF9fRxAQe4Y`pol>UHCt!(8;RsgKz0k;R`=Sr39>vAtj06ipd zp*<>(v?{k<8cd}IN0`AIP>1!AXW?{0g*K*!(!;$*qZ8;6uX&4c@N+peDkwN4Vq~=0 zyCbJgp5i>onTDEXG8u9A^9rDZP!@)4qI(TSr=C>$Mt>@t_Uio-`3NDaBPikX{3HDx zkR+OG1;<%L$|A1$WD#?Zq9BTPu0 zHL{Wt9SnhJLJU@jjR&1ZqlB0%0fjG)4Oa2qe|b1PloB2pOo?!vd zFpnM(1zJ{zQ@ucJu~%exa7d6Bcn8YRk6vE$5%HL{J|a9|{Q`e#06l!Y7m8gfC1U;R z$Y5&3`X#|Zn{i&wNL2eDDM}-8Mn~u<4Sr)6o4an-DRub`ZLqn;- z0j5E{h7uBp`qtWuAZ^wI(XwhYW#j@-9Uj|dQR#+?k;>1}j~*keRW9>KdE)g7p$SWg z^N4d%heDsuHftL822GaS@&;u{s8^~*(NeP_7n|zbLlFRlX?nXI?2lc z2~3%ao9v83gL=lusZ(Z6oi=Owj7hH3rdC>1Pcf?=V6A#VFqQI_W+K*J_I=Gg_Jvuz zANsMxKO)l0KbjKwhA!KPM8GQx%p%~G7b4aU{!jWW)1nh^);Z)&I^WWoo=)qusIqiF znnvDao@o+#ORoV04P9n7>a$lg_4Lxe>^7w9eU2l@1W3RTF@2#s7Y43`JUL!~ljlyn zaqHX-(Zmy@J5*&BK4@+KS7t&89SE9O>p+~d&)jbGHAy;g_LWdui<5@#^V$}nmdhD1_=DCTs(5L)rnA~w-1_VG)x;GmGV ziw;F$4>f*m7KK5uARMg#-d-u1FM6%DNKQ6OLX%!fzO&xF|E$-8j;5-&*Nf`tb@!k3 zzLb1ty%^*iXs;z_z*Nzbj^5>IBRW=E#HS-U)IqTH`J4DVEjVaVF~h6^gMOH%v;co9 zWr2S{B+B*B?}7BFjc7RlUzbJEHd?xS9y&#b&zyRTmO1DQ83LjH!Vs7s{}XL31UEE~ zKxBPu*47{Wm`mFjj3#goIu#j&;;WbG+e*)Gyuf|tV)Uh##iG}JNkm$Kc`o`O8zDmG zQ8xKUQ6oLj1QhQ7hWs-K@u68iFe-o&K5{u)(S@QlPjECvWKs8pSsmB}lgCneAjJzZ zASEP<_HyD_h)Q}HEh5h7yTt+!@e1`1LF?r3NK;!Dn~Fe#=?lm$0RjgBxdNIn6@8Eb zaLh{u^w2lTpDAm&f_kXeHjBJfXM03NMAB&s!jTJBqWSOLo!uhNvuGLrsujpdk@NsM z^>wX>5!zu9``Ro9k>(ZRA4&`{RsJ@=eTDi895T)<2`zzUFZB=Kf&$ST4S!$GP>~F{ zYMNOFW@C@2@Ng7ER-&-}du!X!)dgL+`=}TVadEHr= zRg*qmN!f%hB|-uy5wEaPASNt_EgF=;C}*InfryV#ko36p#gr9@7_nA>#n<2+nxz<; z`mxzFrfVfI;nm4-!#@>3MXwy6j4{^^@n)?Jd8x|C2EK*i!$}*3>J;pvni3wuf z(_Guf=!Zq_zX-8)Z&vz#5!OPOhYUf7)t6t8Qs@OMzr3{cjRAJ^ZBlRSI^El(-q>?f z(U$BREPP9JZGM~78!hC$P3nyn>X8)m_6qbtsR+#qy*w#&8U<~(2Ux9f{_Q%%2W@bI z`ekPIu}Tit(JbT2jQM$6onx-D_=KB}z(>P3ikA=S`tE@ss*#iQV#hwd$` z^bZN5yw?othPD&mqT{_L*YDr1#xz_&-l;U^3jNYw#7=vd$ncB+jYEGTlQIW z%ry54bh)Kp(A^Bd9OxoevKNlbN{18WxrF+do`-# zooe2zQM0jT-?*B43NB~SdJJ|F0_TPVQQvO}eg6(6@716i?@;ny&FMx;V4k&1HPDn;jl z2js*^Gy4oPJIY(U0ClAGWc9i-5cPs~&b4Iu*KL~o58B-U-Inz=tXV<#i<#;l z1&HC@TGv>#E;7R z#zu?$e6u{d0dX0mT4Pc7UeZElmsi$W)LAk>KcsFMJ)GuGUF!d@btWU7&_&MZgF+Z$5KOdc z-dk?~wh)>uM$I=XfZ55EL__`Imdn2i$;oIyfK8TlGi*=2R2dL!W!sy=vRAJ}qyt>P z6n#*;;F4ipa0J?j51_#FKxVrjS(v{4N4FCnTFjemRt@G^tF|)l9j=>>)&XFnmst_o zM6m2GS*!bREd2>p0FJbb26QR^nhM_E-iH=@tx}H{x+fY%^$&kbKmTKhzZmp`P+kS| ztWr;pJ}aZ%)WpuRcJPDXo}R^0$! zzd?()|6;4?$2VwvzkQ}e(aYbHs-si5#Pm_ z?_!Ta&__LSwdC{O$DyUYOcO@0-aX}}W-f1){;z^vACVrVQRZ{q*vpIuXETfA$b}KVlFsaLOrL4wcZIvco zVJHxnb#*UG$8nm*5PXa?oA~RiRt*aw8m0+lDzh~=E z_JcnBig+Ga@ENoE7#)3SJwtxllqe3<9ZL^mE;8=&bgQ8~X2k2- z3J7^MJ`LLvWmnkvr`%aPm=*GVZt9wB0dq_D&}{c%IL-tiV-Z=h)wo;BG1L)$ev%nO zzLXfWjbD(*Xx7K%8|qW&x-+et#msK+dm@AO0f83Zl1k7YBWIN@OQ*B+Dgu4IFM(a_ zBzl;SH8zzD5w`gA`F*-ixH5wbCvqPqrni6He(khy6{lcz?LLkZ!4@hkm)RhrF@%#5 z=eRmo_r9KP_x9*qmr4F`jr|ifn_Uz4H9?o-ifhG*JRTD%5v20^oskbsS@~Y8Yi_Hri+?9!W z(mlUYIwLKM$I-Z6<)|M!k-&Dx+Sa9v;t7W8+S#2c3+eK_c@eWll;do4g+K7Nk=Un4 zNCI$GSG;iDZMk=CajdI+EaOJLyGW+1r4Q#c2Itir7PnS^%}ZbmL)|4FOw8!hs;gwT zdl~`=yP3gx*M2qLEJWTXH>T+}u^YE}`wFLXc4ORRA0mYZjDl>&5qFh}dsc*8Y>-#F zAH~;8w0F{*Q@FIwt6`J06=aTsn~$uiz+F|GgW>qfl_jf@%j3iw$cCD{Uu9o18f|&HD~c5T{{)dWn}-`m zE)+I3UOYoSFBGslZq{y9L9kp^T;|2d#9^)2meUCW99|xD^XP;YA)_RyGD+<2c`w_& zFOe-!G~7**#W3z1RkGWEV4dpRVcZk-lrH5|>S%5qHypC14&Jr$B?QB;F}!kI%hfI9 zJ5ghF`Hjw!>KYWS9fiG(;lHQLK6H%oz~$YvA@Ytst;5Mj5;PM`Y72ob0fQ-IByZU0t16yxwyS*rduWK!1vw{ih zE3TF#$9|M+xapSIx#>d04%KI9u=H5viCgbx(@}c5)JLa^TTy?wy?y4VoJL7hxKkC0 zT<(i)+;(~xvwARh*7n!~%yMKE5%>7wPoxV1_J!82r@B^5Hsd>kJ2kCl<{2`kt5!d) zN|oY?hlWL9cXWnaU>-r~?O{Y^lIXdX?w(zLdPY&wA##zoDt3gSjlEr{*(|ztUY)$}Gbn+h1pobfXwi#ji)bnlF>3Uha?m38mVN*NZ#+3KWgdThL| zJK{Xgn8tLk5i^bh4lze^+!V#Jy9*JriwFS!I zXD(*{!qM5rp@Es|$gF;(twxE#+wD1a8CKY`)Zm*gi@G9qZ18EkL8f)?E+W@%bu#X` zNy6W=^(MB@3Ytl_>(+>v$sK8V+#ot0>!#9k%8Is$l}1G@A?uevJ~HSz4+;3rwf|YwkDFC$+nL(1&6nfKtjp;lI(3K|OQP+6ci6)x&A=zcx9i1> z>k9n>x3&i;u4|?8tz;cO%qW#4k~-SZc&+ilwA;8#jfqah)hqa!WIRoM3d3|&l}iij zJNLRN_vk|m-J7y(XLF3t)hmW~ou~KLheNt+kZ-KE&%28L(GW-?zm|k-jJO;-~|9<`t=8Cdz10_Beg3qtJo=^kkBL74amRxgX7Vca-MI%NEGsX6s$+c24USq#(BN7gb_eBTz92`xLFnpDmm z{DS5%8>z0Fh%(2Tj#{_<%IN{5I%a=tcTts+5QlvzsX~b>HiFYDJ-bTf%VpR%)#e>! zw%$UsX=!1V`y%B}GNnV~wj}Y1eWsBRLtoKxHd_`ECjABn^J(|a1j7)C)Jt9vU`}6+ zM;%1BIo*Sey^YM@omw6yiXTl74&upvkTaXqD#w3x-uL zC|lxvv#Vfs=Pa2#ULv|Z$Jk}$aCGW2vN40hwaMZn+MMLis?n~)_=OgJgtoscQsk#JhPCm~zB)ANLQmuH4}yXPqa*E5Td=y{T`+cT4} z!}By@k7qVvXTb@=F5_y>C&tTcf@6+>?zTqHXEbSrj8yFt#$#<7BTp+~=or7p({E(C zKJ6!W4M^#H+E3xSF(tQphrBExC8lPFqHJS|Ywbe0H^OQcD!dW)?6y1!VRg3^NeHVS zFLyv#{dk1~!el4qvVatOhFp8G{CGd!akh(~acg=ee+O+{b)!5zASHyM)t)RrK8kmk zJyp=SBPfHvo%X0E8TC*~F{564w*2@^-ZAzxK_fRP6N{vtRmc+pQYOWAXb+bkU%@-d zo-AnGFU!Cpsk)2u?tqj%vCUdh`SD<0y2w?~m?Fzm?x5Ay)oSFq0V$=ibDFy}%{%>` zsC4SIvr9Yk=CC}e5Ae9PQpd_jiET;l4VNdXYwApZzGW>2Xq|L8f)Yzy0jg2&25273 z10WwV7D043c$s-U{SmF&(n|eP$->~K*D0?j#>Y7lJ1khjVV*o8; zEd*#C8R|M+;sB6by$zrRthoUB@S(0PB{F~>sV@SwjJ1rKg2QI}N=H=wS*)nDzZJ8l zNzNNbl=4;!_kH=M=aGxk>zDH9|fHpaN|RKquY5Mo>%^2BiGt z9sreTp8}NW{v|-!7?8rt`v4?i8UZ?8g+owhRwqcYb$TUQ9*|7x8YDKS63VPnl%^{ZaJ=K$$Un5R@-Z1E_^r0ML=DQ3&E@ zu`E!68n3Vt>uNr5FQ$@sQ5G1)#$cNn1xjPY4tzu#TV)}`h2ux?I+>ku+lxre_ z_9>PE^c!;@K&UXNl{HwPbtcnNDI0}wB!B&Ps{bH17i(6{(40IidJ ziXd)@GeEWKB>>IC2LR+F`2<14k_P}iP)7o^1WyCVmpBkXk`ga~RO(d#eS@bUNW7K^ zb>T|(0aU0C1ZWXH7@&1Ts4KfeJLS6KdT7W9m41kv5L#UTsv0`7z zKs4Z#byZ39rt%WJG2L+ja*ENHI|PX+%Zw0u7&!-qYJ&Q6kX(UhBBh-5+|LjP|KZV_ z2ufFf36Kvy93T>R5Q4}L#{iV1{s^Ge_;`T)FyL6}!+d~p)sq48!$$%{#tcRf;h_gW zT=gdadEprdGOou!UE+sZ0m@d71jrX33J}Q%VP-sG1JO!QaIPZ`vYN~>7-OpaBAPf| zEi*I|vPxrrZ&KdiJX2n{B;#z7Ndvq)-9n=c6QiRfTLWceINcm=_ySHTGCrI#q7CEG zsYjp;3#Wn6hEZ@zktyLcGTJZ+o#qG1=x|DnHjIN)iYyyWH=d^8e0|0?DepwnkfIv+T_UCh6UP9HYu7Q$&^F&5DFCfy2jT5zivi%<=@LbDY* zX7+3`_VOj!lpGZY>R1zPAOcr$u@aI=YLhMmPKykk$~jGP1DXKW8=Rx1g*hM)(f5`ZGr4*+86S0PBUoB(w>ml6S5qOJufLcbQEl?13Or<4m2RhvEY5K#uC(00jv-0IgO+U2$dI z0J*E(0iv^_sSjo;lP;4)!j0aYc^o06Pwa9fq%@n-%aEMrszVTKe5er8Cw8S0Qd&ak zY=HdL%K%~sA$?+3C?TbJmR16^LLCiIq!7|4){8#SDAde!FXhWBXCW6Bl=wpSYE>Tq zrf-C>a^bI~!^*{}Bn6<`>f-<{62i)bzm5(om-Lch05z(A255m0RxW&>Y*@Jjl_&tJ zP+tOQnQ$3EzDiiQ$m*Jt=rGv$iYos31P62$vCLX_2FWGcB^Xu z@)E*eldi`>U1)z1piK2OfP958*rX&J)D`p47N9h>4j^wl#HTV#1`~XoJQk+JD&}*v zZV}-+^Wq~=e$p72StwrFzw;pRVl!kgdnU*$fVG+_0O$`M%G{Xs`a^b6B*{=5p2de#T}UVOi8x=Q~Tx*HOdK9n}x#2L@3h zf`7N{yJJuhqxax)O3Wu84C=e#m#h^>=ProhHfP;?(r4Tka7-CLe*BJw3zsZH$Hq_h z?OoY-B|1KJk7uy2#wqcgd1p7?*f}_byM3|Sr@#Diee9?qz5f{b#mRnSCpO-ivta7J zZ5bc@nl}8|XxG@=p1nU_d2sZgqcg7l_Qy2Jm!7d-t=#d|s$;XJpO{aRujGC*^zQbX z(~b?9xOw50M|byhM2`NgIC9jk=JwwmSPhFgeWz|8)jwdym?1@<-<($ zF6UsrTYb(iH@+VAV_n9E!CTfY9y_f3)+WKZ-6NVdy2e#VdfSKnbaY zrB4^QGVdo=wI3z)`TWi&KTrMf*IgUGuKipx_FD3tX~&(nR^0KQWV`m(=mVc#ox0}S z&XSBVd)3s9&c5Zv_R;4OgiW<06K33*a%4vF_#-omW)?^MP`G~7&s)~Z`sQBD_j7Fd z#nY{Bh$QyLmZ}yWrl59A%>e#SV@Hke{m0nBKm0KUKjikcf)9S(n)+-URx|$F0XtWw z?)~jZ|GJZNzBsjV_t1yO?bVy-cnp28tNzbF>}bJ6r*9gsIYl0|;onaY#oz4Jw{@n+ zkYa~j8~c-|@^-}E2t3|4-TB}R*EACG6i2{a9=;D=R)Y+8g{EDr(=EC0SbXk+k z8vfM5KH@j>%;JUY+G#+>-kH?F85`}k>dW$IqR7?90V7zgnLB)vBZ8&Y$pLeY5%7frsv=uKDx$#1ReOe(Bfm z=rDZae7g1?zjyylA9)P9`t5=#2f}s*eOkR_)6fUWSE==rlRo@9aKPdn8-`5qx$#}; zfZI%$v&j>wK{m51@<|=j7vHVg?zb6e>)7y{Z_*1^08(+NqXVr&C zKhICv6*QVWCHZ80QU6V?b`i;v+^*9 z^vAeg{tO&BCi(WFPbzND4{2NZ{f~o=e}1FiaYz{Rzgn?P{_ywDqE32#w0rXsuXT6# zmi~DZe>n2Et$$>?jj+LktabinZh!yJ^M~Btd~@=ieoMxtj$cBV<(zzDc>NUqwy!t! z8}N0|ho{%(uNz%)Wr0i1ep=H8hsza(Z2QpdV}^gRb71P&L(ZvnZVnf(56=2DKPn_V zarWa{cTDV^fxOlqlk(~(D!1>yqIa6qx^Ujcy?av!{W01ucp-7z)PeI{&h6Xh_UsG7 zwl9~gSoFz~@qew{^YN22$CedXx2Hy5ordnjt&Io?w0Cfmx2 ztIImhB(a7+`y^rJf z`0K9i|KG*Lj%WWr#YETgK{rSJZ;XlcwX<9=6rqU&#e-}xogwl3xSBC`Y(F$%{1O>B z))plOSIw7brtsQ3^*0&k`K5}K=a$t1JlKHgd33%dz2#N(dBNf6Iihpx%y!oG zM~}EzEO_xyLOCi#4<#IuCZdND3bG${T|U_N-sl6nRzBMK`-(*aKF*Py{L1~{k&k|y z`k-(8iYCH_*{Tik4T(Q|{PF(c53%h@wTabTza?AkqsYjS+;*3#HSzk#aZWj!cy@gEy-0f?f9X2U6Qb^4tSX>qK@qQ}?*U@q%vG`zZLo!A^-k3}rayy0atPF_eRKJ=7{+wY`bO~h*Zks{j?9Mah9pcyyZ3r2vF>iM$x{6t>7 zKsqa0$#V9SO&~C|C5CakJK15!$SD$BOlCpDrQ%$)_rItt>VbQv`}{IBLsKa&kK%OX z%*OqeRqN|2iVHTf!#lBy93+9~2zzR~{M`+ArN_D#-K-3cZ|_vhnug75NS1ts2)9gy z?2bQlvykx^)3SgS-o##efIf_o?_N8du|vW5tDr1VA>PT;OhX-o&Cbsf$6~sOy!+3| z>I89Q*UUZGdStF<9_tRGh^NsBebVO}JqRD-Iv&P69ibqK2XpIhXofKgw2_QfdR(Vh zR~hHGg1`93z41J@kGSTsyJ261U-XvJ=^?qCn(sQ*`d>;Tnxg!cI>yyWNYeC}liBya zP{u#Y#YOpL>xcL)YS=Y%PjS4yneAoh%PMN!&b3F)8Crnt=p$COUK$F4)V%NMd?K$e z?oz$-D{S`G!nWP&u!SrXbqhs$!>Z6oeYS!tlqlC4FOY9>^4(QGq_^L(G2C~?woOcn z&oLZr)}|CdM-{Mtba32Vi|PJ>fcmSc(P?S8zL22a=Fpu%(68y3DP^|UcZ7+m=MzQE zvFU_A&z)-3wQDB$5uUT3*t8FKH9m`I)j6Uq@z!E*nmDZKG%ngtnnY8|ggMgg#bvBe zy}>zYkwd{>GA>!IcBWmjZ;_ywzvIo`(ve_r**&5s2mCRQ{NBp*^9dfG@+hU7z2n>d4o|Tp>iU zxx;5;4H>f~Nzx1TwF;sHL}U zVZpuUWhxtvzq0#e?EwCTb*_gr({POHvWRM3dQA46GoF(8p@!Dl3vJ5oZ%`xMnvE2l z9ZBCesD9w3*n7^rvgc1rn1Y!PALxg3Lcil7>rqR(xQY!YI1Q)xs?QY|F_)|ER5xpr zvTCyk>hXAmbUUtmhBBG^{6~l44C7ZF4ebNfSGcv8NIu3IqNDa0<7d@?ymP}8(g);< z2vN1)32|A;?QEq_tJneEE_}3YQuA}?N7(z~bf=_0rCYO@&)H4a)Xm-ZPL6TV&o=IA zx{(v+Rkm#pkyobrO-HNX7NlGvmrG^skN=p}RL+}Ha4(rlC}HKZx2M+*Acw>}w{h=w z#-(;^QV-q^DadPRyL=@6G3IJ_r$*QHRI2|ZZ;ZGtxaC@M?U@)+!%Xp2Th2|DVdcYp zmniNeZ$IOZ)MQMD-H7gX<%|G*^;z**gB@wcko?5vv#M?k4lf{u}1C%P6))zyPU)Ox^z)K}^KI7a}_!xJl zXu4*OGlW0cd_MqRVo*H7(ld=DvVDdzR=;3)wy~x`@?@Qn<-X=XhOt`O*{7~7UKd@L zt$w`z4mn4h$9^7E);X-kwp79Grsl_^$ZTA4Mxq|rT~nkUXuOfge>T2qebDC)I9)Qv zu#;>^#k30DW1k%7l}N7_#jh(BU*&5U`Zj%%dSCogfBjf~jo-5b2X@=Gn8SuKDEG7| z)mXzeH1cE<$^uHOPkDDN)*QyJ$Ea-;ZkoGvJGORw_cpcEaD+dVed8G6>1tMKyIaLG zCti9Ki@#xZbcSdb;LhUM2R4su_MLphApT5K^wz6p1_$Z z?YNrAN5$1dk(@Kb8Qa9Fs?FPg!RdTBjK{io7N<4HJxzt-)YrN@c5)6yDUG_t zvG&~r_w2$B&TyxWEx(m@oH5pFCsi85SOJ6^QEi4_vLcl93%CHoQ%U#YW^Orsv`7&& z!ky8&PxE!NaRZ@kk0H^)c-t$li^x{^7CYco<4L+e_v^}rL*jbnj9`U*${jj{Q+wq@ua&Dz#YsC49#%GN%znA(M|C*p`)Q;#Z zkfZ6DQxv1WtjjXCWgWeTdnAit1sILnb(n7Bo`K!Qnq7K^Bv*LzELkbL?r$U%EB5L+ zDu(fK>RD-)wutA&RH z%qOzDhjbc~$$49as4?x&5@(#^v==Tn_Qj=^2jL%=1?cthb=rbi``H@_eu^}y_&0k_ zy}~d;p^V?^unbmTwG94VHO{f|9lT^`&d(sq1_|w5x2V^siyuykiL+ovQcgj zQ;pInu7PnnD=CKa^aCa5Ij+hm7oCf3F6}0(Zgx+7;u-CDNo^=&*RW1CXZz-NOUt>W zHbZV>{P50hGFj?ANz5k7E_g}29rrzWWHUeMiCgV%C5I)ob!VLQPAmJ3D@~u2gCjLL z^Ka478gQpWoN9-dac_h~>1$JUtFZpBbcSb~m{_D$8IrUC^sJTuqX5hBkZ8L#SJb#T zB{!v@wJ4FFA)e`Hyq{z6rz2HZ?ihC_iaykr&FPem5u~OB_E4#DFEPQP`%XH_kPLTD zA2inNm7A5FVv&8cH0C!YXCt05tuy9lCFco{UB+d z3O=`+SrX&9TZwN;Smsu9bOP5-)!2401%;K5&QKtebg&(tv~?~jPYm+X48qNlKDd&| ztF1w+DV_2@MLmjrqbNQef5~tfMbsI3LwPzfn$+@}JtnWq|4vwB+YRh2lrfj#tLLZl z?awOF#59PjkYcLCjF-s^x*M=)EUM7bwtZk;!)*seCUx9j`BLR{KSHbDiMDb_k?~o# zcoJcDekVUns_x{s%^o2#wzH+hdzA#1s5Wl-o`Ic|693zs{N}CO)0OjTy1kmJw>mtz z*roiWyXJ+-HQ6+iR6n@#4ewe6202Tv=nzmDjSYKM`B39F5U`fa{pq+;T(~ug$61lxL6YXt&Ah7RN=o z8!%sS?7sTlauHX89=z^vx$t@QsheA-N3y4*r*3$%p$n=XqGxSFyeeH*qK9sr=YLm8 z+xbwiFEgZ4UOc_y&d`88Go{M*i^hG^H(dF?_tD*f?RNuya&&HAd$_--aMO8b(-LH78? z4L%;-DbI~VC&xwG*x@~+2ioC-qOo>(@8}73_?T!{J3KMk!wyf6Cfec2(Y|(gPIQo+ za87iDop3`m$48wF%L;YtC;P8crOVkcZA2(!aa zmaL205*Nf_mgKDy#Mud%Ko$Vm1Y`}6Xduggga9D{VFQ^9#2?5CAYnij0tp7<10)v6 zd>|ActARuTSqg*(#19AyhzF1WAYMQsfGl=giwu;lsFa?kU#=@zu+9VXS9JE_7zrYs zw}9LM0%e`WKyCvm0#XPB_%OhS0X~x~2KX?*hXFne@L_Q?*xRM2~WPvML;7S&_k_E10fh$?yN*1`11+HX)D_P)5 z7Pt~WgTLPFMe#*?Zs6szySbZy)IHcj$B(TP{w%@+83V)_$OIs>fj9wi1%d-YK!iOK z$Yda20+|5>w6LcE`5K5DkkLS<0{IHaEFj~6xB!_1#2v^eAX6||!t>}F<_E_^Wbi5( zyiW%2lZ!mQqvK6#Cjuwf$_886U@IGJWrMA3u$2wAvcXn1*lGY<4PdJQY&C$b2C&rt zwi-aG0c2w0Ja*yRs+~-09y@Ut0TI3hAdQ_c+PF8KtgQLX|CY=2Mn{+~F=_{$7L$xu$s02m=Fmiy=_!}_Jgen&U zV;L~gfl&&K9AGq>7)=_X)dnIkN`Wx}kw=96v5e=L&(ji0kM7nrS2lp~=RghuSqr2b z$P7*QBVTR`s*jrlgpUaE7a#|LfFZ!*d%7ZTDL6`EN zOG(hBBGf7}33CtvcnItfi1ZI-J%qlRm3e2nm zGpoSNDloGO%&Y=4tH8`EFtZBGtO7Huz|1NzvkJ_t0yC??%qlRm3e2nmGpoSNsxL4Q zl4#IWO;$Ph$i&_@P+WY9+jePqx_27P4EM+SXl&_@P+WY8xCeNxaT1%0O3 z;CY(fzP(6u07g+M5CJ00Z_&tN#R2gI5(LB>NDL4dcPu&(G7t_T`Z+*009g(s6bNY1 zZw9g!2m{DGAb~(u0f_{%1PB$#dLZ#Yz5%ik5t`ENyKax}j+Ial4rCFK?-2RlkQtF5 zD`$6)*j%pNPL&>_t;-p)Mc`M|ajIg$^vYwKd>>LdhiJ=k(t-sOi#qBy~eb6UvIU)|H|HdAGCoF&ugP?am(I4{3_@w3L`QqoHEM-{)hYaVHUCf zaNmBxcV<&=et`aX$LckKB=i$ZyNG@=diy<^y=>Tu&%eJ{F~m2Z-`D<&7C8)>^ySxq zqilV)=Sg?;iJd{mjan2=9P57Qv~!<3cQ}O0hk~`Lh?w@6SEPT^Q;8B*OM{DRvI#F zx_?JHaX0nnOkB}^rF~*bw${GBKB!*lOvca1wzlJrW^!|a`buJI*;bWBu1T~O^LRC8 z&nq>(V)`pBxwFF5GMp#Yy1082KGYjOBg?w3f4IOTa5c3ZJk%0y`eE%TUZVPnpZe(q zD;K=3X?Nx#N9s2rA2aq#UpAx18eYmfwX~!%pT;)fpuwkgBFEIfbp*7%Xxm%0T#abx z=~)I|``Z4_uyJvHbN@U}f6>aaqi=Cq`Ffw%cFtAS3j@|-@`kXi{rC^3MFjn`bJlyl z4A7@VR?2$(_%|&be{;xH<2~!}%^UG{6HFt5Dz%EVmbh1H$bFj^tRt!WFXq!MW~eT( zDZzeOZBCb)XgjXXvlypA#xM z0wdK}VaTy6kWh^nPjT-B_NUe~SQ7J|kI~IYl#Jm`OfP;3<_A>B=#4JN2W81B7ThZr zWG>iEOAot$aJS{xU}m4E8I;sAalwUxLuFM#v3%uN)vn3cJ_^9NE^sZryW(6ND?@7KPtMjZJ>`09 zk85VtnAues%#o;1}S!QPwBmEMxM|SBuL2Q!zNlvYwLS9k-a2bIXOl zD&~$0e|^kfF8q}-#V-7HF}Gd#YhsFA_@tOZ7ygPEi3{JS6Y;I?M0|doh|jAN@%eTl zKJQM%NA4_iAusQgxRBRkZn={Fy<8v7Dy zKaG8yltyCBTPMb@baV;x+Ul z7cq%m=ptT0m$-<1l($^OtCe?L#OUEm7qOQT@%btdpSKe6k(GrmoaIW13ui6%mJ4SU z_Kpi@J@zjb&Pr^t3uhhnwhLzsw#bD;!XmyESj6YUM|`XKh|iCY_`LXtPwc*rW|+%z zsnq3b*9$bBY7JGDSW+p&Vq~r@4InuV{_DYOd& z8dr4@RYh0$R@6&Na&wBr*~-8a<8motJS$3~8N678N}WtQSD?YE^QkJb!n2}YT*A#M z;$-s?;|c;|%wZ#q-g>u6ol-krpdqLWsX4~j-^r8|&kS-Ql#K5Y`XD zs(TRDoT;hIZZ~a9C%aF;8styzYGGWoTeP~lqIE{e<)DIEQ=vB$vZ3$;C>#id#8prj z28CmxaG$Ag4HU*g;UFmVG8LM1MnT~;D12ZlH0fkPVSgxeE*gwH1%;f;&_EH$X-*?$4lv9~XQ1#f6mByWo`k|b zq3|pe-hjd+6X!W7JPC!zOob*+0Tf<@!a7C&iu%FwU5F@_RMhX6-$6w2bw&MZc?Kd1 zYDIksm@8ggaO*TCE#Sg*I4jGDprn+YiU>+6%TfiVl$}&O9rAhl}RmUf#&dHH=& znHS+Vy)tjH-^5B?nmp#pWqXoVucq^f!;0xocY*tz6^<44IdUE%3Q|RVguDt7g?mN4N-jb~!LF#cl^YOI^scB+ zl>dN;Vs1tKV)<1>6r(EY3+0CqQTSKXPmot5q8Lz7e_H-4B8n9i^;_ik5K&C2s8`6( zBBBVZs2?V8MMTlBqCQ2w0};i-ih5u9ZA28~E9xudCoAe7oX(ZxqR}221b*~W-=V6W zs57W4yt;|15~@?FDwg^&75z3?9#z#xt)r^8s(+-aJk-~y=-0rGP*r2pHB{A6^g3)ou`IYPHJ+ntj?(f#!ksTY)A=yIG)7 zY1axgiCTt0Q>dLM(45u=3N#AsDuE_N8!6CqtCOiJj6A2J9$P9qS421^2}AV$5Wx(hPC>v6VXX3JZaTug;;W z#By0hJ*NzJuE=fo+~g!LU1Zid36rot6axCsi^ zLScZZ&u8!RlbxsW38}uN8i)Avs*W)NlrsDsv7>- zT5>s+dWM;}53@!|-+5JDv~=cLVa`d!G+n(x%{z{m?*56Gl0(igCp^HUR~7UxNjVrc zLv6q5lt_Ok|D+PXSiL)F#4=V{#VPvC?ofGm(F#@$-7a19AhjendCLqnzCKHI)h+*| zu|s9ACw#Hz&uQoi`{nU0>g7JdAnSdEo;N(2b`z#qalNveu*eE9KM4q`V=bky$MB}d z|4gs8qEgYHfux+Vu>XhK2W1v9c=24n9&;mtVFv*Yw2AP~!w@D zS^T1Xfry8VfS#kjwR=`uB;bv5=D!&V)?0I!@5hB%Y5o|Vdki#P5twT2QEd7te^X6Y zEgH~!%`G*-?u>O!J#ByTzI81}Ug$YqX_b@3J?KwvSn4vpKo?$?X?Z+!^%@T=7rwPc zJvIFEr-5O9;njEqp+7>4{@TxygPLQe4l}*<%knorCr_GA>($%*o=*Vbtydze+F$fk z0|GA~oju1ZufzHT0npv(^AYhvI0etn1$3KE1U^)w5jOSc#A*Z=pgI6G~z zW`1g!%s3)S9{BE>#3#jql92`kO9b$R+E4)*&*Rt5xj2zG$W&xne{;#~U z6*?mnUi!M*O7`QIuVpoj4G*jLq`lIe9Y_dz#p(@-Jug#z=gS0N_kLa%ra3&OBAu4- z=+XBcuM$Lm_6^CKa+z5kQr^2?hl-fc@j7#^Tg3ssT0k10)90Htpx?iH@kZZ&`{voZ7L}G}nnKF_cNyQV z@y+l49L+KtX{FH;O7El5>J`0~w~00qqAVJ%n%xr(Z&%q94)gx;hIH=5wx+VBp>Nmneyu5BU4`|F4d& Ub-TW+Qdh8>8vEBqamU;KA6qd1wEzGB diff --git a/Samples/Utils/LightProbeViewer/LightProbeViewer.cpp b/Samples/Utils/LightProbeViewer/LightProbeViewer.cpp deleted file mode 100644 index e8bb1dacb..000000000 --- a/Samples/Utils/LightProbeViewer/LightProbeViewer.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "LightProbeViewer.h" -#include "Data/HostDeviceSharedMacros.h" - -const std::string LightProbeViewer::kEnvMapName = "LightProbes/10-Shiodome_Stairs_3k.dds"; - -Material::SharedPtr generateMaterial(const std::string& name, uint32_t roughI, uint32_t roughN, uint32_t metalI, uint32_t metalN) -{ - Material::SharedPtr pMat = Material::create(name); - - float roughness = float(roughI) / float(roughN - 1); - float metallic = float(metalI) / float(metalN - 1); - - pMat->setShadingModel(ShadingModelMetalRough); - pMat->setBaseColor(vec4(1)); - pMat->setSpecularParams(vec4(0, roughness, metallic, 0)); - - return pMat; -} - -void fillScene(Scene::SharedPtr pScene, const std::string& modelName, uint32_t width, uint32_t height) -{ - assert(pScene != nullptr); - - float offsetStep = 0.75f; - const vec3 origin = vec3(-float(width) / 2.0f, float(height) / 2.0f, 0.0f) * offsetStep; - - vec3 posW = origin; - for (uint32_t row = 0; row < height; row++) - { - posW.x = origin.x; - for (uint32_t col = 0; col < width; col++) - { - std::string instName = std::to_string(col) + "_" + std::to_string(row); - Model::SharedPtr pModel = Model::createFromFile(modelName.c_str()); - - // Scale to about unit size - float scaling = 0.5f / pModel->getRadius(); - - Material::SharedPtr pMat = generateMaterial(instName, col, width, row, height); - pModel->getMesh(0)->setMaterial(pMat); - pScene->addModelInstance(pModel, instName, posW, vec3(), vec3(scaling)); - - posW.x += offsetStep; - } - posW.y -= offsetStep; - } -} - -void LightProbeViewer::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpCamera = Camera::create(); - mCameraController.attachCamera(mpCamera); - - mpState = GraphicsState::create(); - - mpProgram = GraphicsProgram::createFromFile("LightProbeViewer.ps.hlsl", "", "main"); - mpState->setProgram(mpProgram); - - // States - mpRasterizerState = RasterizerState::create(RasterizerState::Desc().setCullMode(RasterizerState::CullMode::Back)); - mpDepthState = DepthStencilState::create(DepthStencilState::Desc().setDepthTest(true)); - mpState->setRasterizerState(mpRasterizerState); - mpState->setDepthStencilState(mpDepthState); - - mpVars = GraphicsVars::create(mpProgram->getActiveVersion()->getReflector()); - mpLinearSampler = Sampler::create(Sampler::Desc().setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear)); - - mpScene = Scene::create(); - mpSceneRenderer = SceneRenderer::create(mpScene); - - fillScene(mpScene, "UnitSphere.fbx", 8, 3); - resetCamera(); - - pSample->setDefaultGuiSize(250, 250); - - updateLightProbe(LightProbe::create(pRenderContext, kEnvMapName, true, ResourceFormat::RGBA16Float, mDiffuseSamples, mSpecSamples)); -} - -void LightProbeViewer::updateLightProbe(LightProbe::SharedPtr pLightProbe) -{ - mpLightProbe = pLightProbe; - mpLightProbe->setSampler(mpLinearSampler); - - if (mpScene->getLightProbeCount() > 0) - { - assert(mpScene->getLightProbeCount() == 1); - mpScene->deleteLightProbe(0); - } - - mpScene->addLightProbe(mpLightProbe); - - if (mpSkyBox == nullptr || pLightProbe->getOrigTexture()->getSourceFilename() != mpSkyBox->getTexture()->getSourceFilename()) - { - mpSkyBox = SkyBox::create(pLightProbe->getOrigTexture()->getSourceFilename()); - } -} - -void LightProbeViewer::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - pGui->addText("Press 'R' to reset scene camera"); - - if (pGui->addButton("Load Light Probe")) - { - std::string filename; - FileDialogFilterVec filters = { {"hdr"}, {"exr"} }; - if (openFileDialog(filters, filename)) - { - updateLightProbe(LightProbe::create(pSample->getRenderContext(), filename, true, ResourceFormat::RGBA16Float, mDiffuseSamples, mSpecSamples)); - } - } - if (mpLightProbe != nullptr) - { - std::string diffText = "Diffuse Sample Count: " + std::to_string(mpLightProbe->getDiffSampleCount()); - pGui->addText(diffText.c_str()); - int32_t diffSamples = int32_t(mDiffuseSamples); - if (pGui->addIntVar("Diffuse##Samples", diffSamples, 1, 128 * 1024)) - { - mDiffuseSamples = uint32_t(diffSamples); - } - - std::string specText = "Spec Sample Count: " + std::to_string(mpLightProbe->getSpecSampleCount()); - pGui->addText(specText.c_str()); - int32_t specSamples = int32_t(mSpecSamples); - if (pGui->addIntVar("Specular##Samples", specSamples, 1, 32 * 1024)) - { - mSpecSamples = uint32_t(specSamples); - } - - if (pGui->addButton("Apply")) - { - if (mDiffuseSamples != mpLightProbe->getDiffSampleCount() || mSpecSamples != mpLightProbe->getSpecSampleCount()) - { - updateLightProbe(LightProbe::create(pSample->getRenderContext(), mpLightProbe->getOrigTexture(), mDiffuseSamples, mSpecSamples)); - } - } - - pGui->addText("Specular Viewport Mip Level"); - pGui->addIntVar("##SpecMip", mSpecMip, 0, mpLightProbe->getSpecularTexture()->getMipCount() - 1); - } -} - -void LightProbeViewer::onDataReload(SampleCallbacks* pSample) -{ -} - -void LightProbeViewer::onDroppedFile(SampleCallbacks* pSample, const std::string& filename) -{ - updateLightProbe(LightProbe::create(pSample->getRenderContext(), filename, true, ResourceFormat::RGBA16Float, mDiffuseSamples, mSpecSamples)); -} - -void LightProbeViewer::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - if (mpLightProbe != nullptr) - { - mCameraController.update(); - mpState->setFbo(pTargetFbo); - - // Where to render the scene to - uvec4 destRect = (mSelectedView == Viewport::Scene) ? mMainRect : mRects[(uint32_t)Viewport::Scene]; - float w = (float)destRect.z - (float)destRect.x; - float h = (float)destRect.w - (float)destRect.y; - mpState->setViewport(0, GraphicsState::Viewport((float)destRect.x, (float)destRect.y, w, h, 0.0f, 1.0f)); - - pRenderContext->pushGraphicsVars(mpVars); - pRenderContext->pushGraphicsState(mpState); - - mpSceneRenderer->renderScene(pRenderContext, mpCamera.get()); - mpSkyBox->render(pRenderContext, mpCamera.get()); - - pRenderContext->popGraphicsVars(); - pRenderContext->popGraphicsState(); - - pRenderContext->blit(mpLightProbe->getOrigTexture()->getSRV(0, 1), pTargetFbo->getRenderTargetView(0), uvec4(-1), (mSelectedView == Viewport::Orig) ? mMainRect : mRects[(uint32_t)Viewport::Orig]); - pRenderContext->blit(mpLightProbe->getDiffuseTexture()->getSRV(0, 1), pTargetFbo->getRenderTargetView(0), uvec4(-1), (mSelectedView == Viewport::Diffuse) ? mMainRect : mRects[(uint32_t)Viewport::Diffuse]); - pRenderContext->blit(mpLightProbe->getSpecularTexture()->getSRV(mSpecMip, 1), pTargetFbo->getRenderTargetView(0), uvec4(-1), (mSelectedView == Viewport::Specular) ? mMainRect : mRects[(uint32_t)Viewport::Specular], Sampler::Filter::Point); - - renderInfoText(pSample); - } -} - -void LightProbeViewer::renderInfoText(SampleCallbacks* pSample) -{ - pSample->renderText("Click a viewport to expand", vec2(mMainRect.z + 5, 5)); - -#define bottom_left(viewport) vec2(mRects[(uint32_t)viewport].x, mRects[(uint32_t)viewport].w) + vec2(5.0f, -20.0f) // bottom left plus offset -#define dim_to_string(texture) std::string(std::to_string(texture->getWidth()) + " x " + std::to_string(texture->getHeight())) - - pSample->renderText("Scene", bottom_left(Viewport::Scene)); - pSample->renderText("Original - " + dim_to_string(mpLightProbe->getOrigTexture()), bottom_left(Viewport::Orig)); - pSample->renderText("Diffuse - " + dim_to_string(mpLightProbe->getDiffuseTexture()), bottom_left(Viewport::Diffuse)); - pSample->renderText("Specular - " + dim_to_string(mpLightProbe->getSpecularTexture()), bottom_left(Viewport::Specular)); - -#undef bottom_left -#undef dim_to_string -} - -bool LightProbeViewer::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - bool bHandled = mCameraController.onKeyEvent(keyEvent); - if (bHandled == false) - { - if (keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - switch (keyEvent.key) - { - case KeyboardEvent::Key::R: - resetCamera(); - bHandled = true; - break; - } - } - } - return bHandled; -} - -bool LightProbeViewer::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - Fbo::SharedPtr pFbo = pSample->getCurrentFbo(); - uvec2 pos(mouseEvent.pos * vec2(pFbo->getWidth(), pFbo->getHeight())); - - // If clicked to the right of main viewport (i.e. the sidebar) - if (pos.x > mMainRect.z && (mouseEvent.type == MouseEvent::Type::LeftButtonUp || mouseEvent.type == MouseEvent::Type::LeftButtonDown)) - { - // Find clicked region - Viewport selected = Viewport::Count; - for (uint32_t i = 0; i < (uint32_t)Viewport::Count; i++) - { - uvec4 curr = mRects[i]; - if (pos.x > curr.x && pos.x < curr.z && pos.y > curr.y && pos.y < curr.w) - { - selected = (Viewport)i; - break; - } - } - - if (selected != Viewport::Count) - { - if (mouseEvent.type == MouseEvent::Type::LeftButtonUp) - { - mSelectedView = selected; - mpCamera->setAspectRatio(mSelectedView == Viewport::Scene ? ((float)mMainRect.z / (float)mMainRect.w) : 1.0f); - } - - return true; - } - } - else if (mSelectedView == Viewport::Scene) - { - return mCameraController.onMouseEvent(mouseEvent); - } - - return false; -} - -void LightProbeViewer::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - float h = (float)height; - float w = (float)width; - - mpCamera->setFocalLength(21.0f); - - uint32_t viewportSize = height / (uint32_t)Viewport::Count; - uint32_t left = width - viewportSize; - uint32_t top = 0; - - // Left, Top, Right, Down - for (uint32_t i = 0; i < (uint32_t)Viewport::Count; i++) - { - mRects[i] = uvec4(left, top, width, top + viewportSize); - top += viewportSize; - } - - mMainRect = uvec4(0, 0, left, height); - - float aspectRatio = (mSelectedView == Viewport::Scene) ? (float)left / (float)height : 1.0f; - mpCamera->setAspectRatio(aspectRatio); -} - -void LightProbeViewer::resetCamera() -{ - if (mpScene) - { - // update the camera position - float radius = mpScene->getRadius(); - mpCamera->setPosition(vec3(0.0f, 0.0f, radius * 1.5f)); - mpCamera->setTarget(vec3()); - mpCamera->setUpVector(glm::vec3(0, 1, 0)); - mpCamera->setDepthRange(0.001f, radius * 10.0f); - - // Update the controllers - mCameraController.setCameraSpeed(radius * 0.25f); - } -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - LightProbeViewer::UniquePtr pRenderer = std::make_unique(); - - SampleConfig config; - config.windowDesc.title = "Light Probe Viewer"; - config.windowDesc.resizableWindow = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Utils/LightProbeViewer/LightProbeViewer.h b/Samples/Utils/LightProbeViewer/LightProbeViewer.h deleted file mode 100644 index 4ad7f6ecd..000000000 --- a/Samples/Utils/LightProbeViewer/LightProbeViewer.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class LightProbeViewer : public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - void onDataReload(SampleCallbacks* pSample) override; - void onDroppedFile(SampleCallbacks* pSample, const std::string& filename) override; - -private: - void resetCamera(); - void updateLightProbe(LightProbe::SharedPtr pLightProbe); - void renderInfoText(SampleCallbacks* pSample); - - static const std::string kEnvMapName; - - uint32_t mDiffuseSamples = LightProbe::kDefaultDiffSamples; - uint32_t mSpecSamples = LightProbe::kDefaultSpecSamples; - int32_t mSpecMip = 0; - - enum class Viewport - { - Scene, - Orig, - Diffuse, - Specular, - Count - }; - - // Viewport coordinates - uvec4 mMainRect; - std::array mRects; - Viewport mSelectedView; - - Camera::SharedPtr mpCamera; - Model::SharedPtr mpModel = nullptr; - SixDoFCameraController mCameraController; - - SkyBox::SharedPtr mpSkyBox; - - Scene::SharedPtr mpScene; - SceneRenderer::SharedPtr mpSceneRenderer; - LightProbe::SharedPtr mpLightProbe; - - GraphicsProgram::SharedPtr mpProgram = nullptr; - GraphicsVars::SharedPtr mpVars = nullptr; - GraphicsState::SharedPtr mpState = nullptr; - - Sampler::SharedPtr mpLinearSampler = nullptr; - DepthStencilState::SharedPtr mpDepthState = nullptr; - RasterizerState::SharedPtr mpRasterizerState = nullptr; -}; diff --git a/Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj b/Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj deleted file mode 100644 index 6e21cf472..000000000 --- a/Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj +++ /dev/null @@ -1,100 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - - - - - - - true - true - - - - {9D80D89D-D029-4E3E-BCA8-424ECC1F1DF5} - Win32Proj - MaterialExplorer - 10.0.17763.0 - LightProbeViewer - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj.filters b/Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj.filters deleted file mode 100644 index 9a19f2735..000000000 --- a/Samples/Utils/LightProbeViewer/LightProbeViewer.vcxproj.filters +++ /dev/null @@ -1,19 +0,0 @@ - - - - - {8f4b49db-cf13-4168-abde-51760776cfd1} - - - - - - - - - - - Data - - - \ No newline at end of file diff --git a/Samples/Utils/ModelViewer/ModelViewer.cpp b/Samples/Utils/ModelViewer/ModelViewer.cpp deleted file mode 100644 index 75e2f4f76..000000000 --- a/Samples/Utils/ModelViewer/ModelViewer.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "ModelViewer.h" - -const std::string ModelViewer::skDefaultModel = "Arcade/Arcade.fbx"; - -void ModelViewer::deleteCulledMeshes() -{ - if(mpModel) - { - CpuTimer timer; - timer.update(); - mpModel->deleteCulledMeshes(mpCamera.get()); - timer.update(); - - setModelString(true, timer.getElapsedTime()); - } -} - -CameraController& ModelViewer::getActiveCameraController() -{ - switch(mCameraType) - { - case ModelViewer::ModelViewCamera: - return mModelViewCameraController; - case ModelViewer::FirstPersonCamera: - return mFirstPersonCameraController; - case ModelViewer::SixDoFCamera: - return m6DoFCameraController; - default: - should_not_get_here(); - return m6DoFCameraController; - } -} - -void ModelViewer::setModelString(bool isAfterCull, float loadTime) -{ - mModelString = isAfterCull ? "Mesh culling" : "Loading"; - mModelString += " took " + std::to_string(loadTime) + " seconds.\n"; - mModelString += "Model has " + std::to_string(mpModel->getVertexCount()) + " vertices, "; - mModelString += std::to_string(mpModel->getIndexCount()) + " indices, "; - mModelString += std::to_string(mpModel->getPrimitiveCount()) + " primitives, "; - mModelString += std::to_string(mpModel->getMeshCount()) + " meshes, "; - mModelString += std::to_string(mpModel->getInstanceCount()) + " mesh instances, "; - mModelString += std::to_string(mpModel->getMaterialCount()) + " materials, "; - mModelString += std::to_string(mpModel->getTextureCount()) + " textures, "; - mModelString += std::to_string(mpModel->getBufferCount()) + " buffers.\n"; -} - -void ModelViewer::loadModelFromFile(const std::string& filename, ResourceFormat fboFormat) -{ - CpuTimer timer; - timer.update(); - - Model::LoadFlags flags = Model::LoadFlags::None; - if (mGenerateTangentSpace == false) - { - flags |= Model::LoadFlags::DontGenerateTangentSpace; - } - - flags |= isSrgbFormat(fboFormat) ? Model::LoadFlags::None : Model::LoadFlags::AssumeLinearSpaceTextures; - mpModel = Model::createFromFile(filename.c_str(), flags); - - if(mpModel == nullptr) - { - msgBox("Could not load model"); - return; - } - resetCamera(); - - float radius = mpModel->getRadius(); - float lightHeight = max(1.0f + radius, radius*1.25f); - mpPointLight->setWorldPosition(glm::vec3(0, lightHeight, 0)); - timer.update(); - - mActiveAnimationID = kBindPoseAnimationID; - setModelString(false, timer.getElapsedTime()); - - mpModel->bindSamplerToMaterials(mUseTriLinearFiltering ? mpLinearSampler : mpPointSampler); -} - -void ModelViewer::loadModel(ResourceFormat fboFormat) -{ - std::string Filename; - if(openFileDialog(Model::kFileExtensionFilters, Filename)) - { - loadModelFromFile(Filename, fboFormat); - } -} - -void ModelViewer::saveModel() -{ - if(mpModel == nullptr) - { - msgBox("No model was loaded. Nothing to save"); - return; - - } - std::string filename; - if (saveFileDialog({ {"bin", "Binary Model"} }, filename)) - { - mpModel->exportToBinaryFile(filename); - } -} - -void ModelViewer::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - // Load model group - if (pGui->addButton("Load Model")) - { - loadModel(pSample->getCurrentFbo()->getColorTexture(0)->getFormat()); - } - if (pGui->beginGroup("Load Options")) - { - pGui->addCheckBox("Generate Tangent Space", mGenerateTangentSpace); - if (pGui->addButton("Export Model To Binary File")) - { - saveModel(); - } - if (pGui->addButton("Delete Culled Meshes")) - { - deleteCulledMeshes(); - } - pGui->endGroup(); - } - - pGui->addSeparator(); - pGui->addCheckBox("Wireframe", mDrawWireframe); - if (pGui->addCheckBox("TriLinear Filtering", mUseTriLinearFiltering)) - { - mpModel->bindSamplerToMaterials(mUseTriLinearFiltering ? mpLinearSampler : mpPointSampler); - } - - Gui::DropdownList cullList; - cullList.push_back({ 0, "No Culling" }); - cullList.push_back({ 1, "Backface Culling" }); - cullList.push_back({ 2, "Frontface Culling" }); - pGui->addDropdown("Cull Mode", cullList, mCullMode); - - if (pGui->beginGroup("Lights")) - { - pGui->addRgbColor("Ambient intensity", mAmbientIntensity); - if (pGui->beginGroup("Directional Light")) - { - mpDirLight->renderUI(pGui); - pGui->endGroup(); - } - if (pGui->beginGroup("Point Light")) - { - mpPointLight->renderUI(pGui); - pGui->endGroup(); - } - pGui->endGroup(); - } - - Gui::DropdownList cameraDropdown; - cameraDropdown.push_back({ ModelViewCamera, "Model-View" }); - cameraDropdown.push_back({ FirstPersonCamera, "First-Person" }); - cameraDropdown.push_back({ SixDoFCamera, "6 DoF" }); - - pGui->addDropdown("Camera Type", cameraDropdown, (uint32_t&)mCameraType); - - if(mpModel) - { - renderModelUI(pGui); - } -} - -void ModelViewer::renderModelUI(Gui* pGui) -{ - bool bAnim = mpModel && mpModel->hasAnimations(); - static const char* animateStr = "Animate"; - static const char* activeAnimStr = "Active Animation"; - - if(bAnim) - { - pGui->addCheckBox(animateStr, mAnimate); - Gui::DropdownList list; - list.resize(mpModel->getAnimationsCount() + 1); - list[0].label = "Bind Pose"; - list[0].value = kBindPoseAnimationID; - - for(uint32_t i = 0; i < mpModel->getAnimationsCount(); i++) - { - list[i + 1].value = i; - list[i + 1].label = mpModel->getAnimationName(i); - if(list[i + 1].label.size() == 0) - { - list[i + 1].label = std::to_string(i); - } - } - - if (pGui->addDropdown(activeAnimStr, list, mActiveAnimationID)) - { - mpModel->setActiveAnimation(mActiveAnimationID); - } - } - - const float minDepth = mpModel->getRadius() * 1 / 1000; - if(pGui->beginGroup("Depth Range")) - { - pGui->addFloatVar("Near Plane", mNearZ, minDepth, mpModel->getRadius() * 15, minDepth * 5); - pGui->addFloatVar("Far Plane", mFarZ, minDepth, mpModel->getRadius() * 15, minDepth * 5); - pGui->endGroup(); - } -} - -void ModelViewer::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - mpCamera = Camera::create(); - mpProgram = GraphicsProgram::createFromFile("ModelViewer.ps.hlsl", "", "main"); - - // create rasterizer state - RasterizerState::Desc wireframeDesc; - wireframeDesc.setFillMode(RasterizerState::FillMode::Wireframe); - wireframeDesc.setCullMode(RasterizerState::CullMode::None); - mpWireframeRS = RasterizerState::create(wireframeDesc); - - RasterizerState::Desc solidDesc; - solidDesc.setCullMode(RasterizerState::CullMode::None); - mpCullRastState[0] = RasterizerState::create(solidDesc); - solidDesc.setCullMode(RasterizerState::CullMode::Back); - mpCullRastState[1] = RasterizerState::create(solidDesc); - solidDesc.setCullMode(RasterizerState::CullMode::Front); - mpCullRastState[2] = RasterizerState::create(solidDesc); - - // Depth test - DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(false); - mpNoDepthDS = DepthStencilState::create(dsDesc); - dsDesc.setDepthTest(true); - mpDepthTestDS = DepthStencilState::create(dsDesc); - - mModelViewCameraController.attachCamera(mpCamera); - mFirstPersonCameraController.attachCamera(mpCamera); - m6DoFCameraController.attachCamera(mpCamera); - - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - mpPointSampler = Sampler::create(samplerDesc); - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpLinearSampler = Sampler::create(samplerDesc); - - mpDirLight = DirectionalLight::create(); - mpPointLight = PointLight::create(); - mpDirLight->setWorldDirection(glm::vec3(0.13f, 0.27f, -0.9f)); - - mpProgramVars = GraphicsVars::create(mpProgram->getReflector()); - mpGraphicsState = GraphicsState::create(); - mpGraphicsState->setProgram(mpProgram); - - loadModelFromFile(skDefaultModel, pRenderContext->getGraphicsState()->getFbo()->getColorTexture(0)->getFormat()); -} - -void ModelViewer::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - mpGraphicsState->setFbo(pTargetFbo); - - if(mpModel) - { - mpCamera->setDepthRange(mNearZ, mFarZ); - getActiveCameraController().update(); - - // Animate - if(mAnimate) - { - PROFILE("animate"); - mpModel->animate(pSample->getCurrentTime()); - } - - // Set render state - if(mDrawWireframe) - { - mpGraphicsState->setRasterizerState(mpWireframeRS); - mpGraphicsState->setDepthStencilState(mpNoDepthDS); - mpProgramVars["PerFrameCB"]["gConstColor"] = true; - } - else - { - mpGraphicsState->setRasterizerState(mpCullRastState[mCullMode]); - mpGraphicsState->setDepthStencilState(mpDepthTestDS); - mpProgramVars["PerFrameCB"]["gConstColor"] = false; - - ConstantBuffer* pCB = mpProgramVars["PerFrameCB"].get(); - mpDirLight->setIntoProgramVars(mpProgramVars.get(), pCB, "gDirLight"); - mpPointLight->setIntoProgramVars(mpProgramVars.get(), pCB, "gPointLight"); - } - - mpProgramVars["PerFrameCB"]["gAmbient"] = mAmbientIntensity; - mpGraphicsState->setProgram(mpProgram); - pRenderContext->setGraphicsState(mpGraphicsState); - pRenderContext->setGraphicsVars(mpProgramVars); - ModelRenderer::render(pRenderContext, mpModel, mpCamera.get()); - } - - pSample->renderText(mModelString, glm::vec2(10, 30)); -} - -bool ModelViewer::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - bool bHandled = getActiveCameraController().onKeyEvent(keyEvent); - if(bHandled == false) - { - if(keyEvent.type == KeyboardEvent::Type::KeyPressed) - { - switch(keyEvent.key) - { - case KeyboardEvent::Key::R: - resetCamera(); - bHandled = true; - break; - } - } - } - return bHandled; -} - -bool ModelViewer::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - return getActiveCameraController().onMouseEvent(mouseEvent); -} - -void ModelViewer::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - float h = (float)height; - float w = (float)width; - - mpCamera->setFocalLength(21.0f); - float aspectRatio = (w / h); - mpCamera->setAspectRatio(aspectRatio); -} - -void ModelViewer::resetCamera() -{ - if(mpModel) - { - // update the camera position - float Radius = mpModel->getRadius(); - const glm::vec3& ModelCenter = mpModel->getCenter(); - glm::vec3 CamPos = ModelCenter; - CamPos.z += Radius * 5; - - mpCamera->setPosition(CamPos); - mpCamera->setTarget(ModelCenter); - mpCamera->setUpVector(glm::vec3(0, 1, 0)); - - // Update the controllers - mModelViewCameraController.setModelParams(ModelCenter, Radius, 3.5f); - mFirstPersonCameraController.setCameraSpeed(Radius*0.25f); - m6DoFCameraController.setCameraSpeed(Radius*0.25f); - - mNearZ = std::max(0.1f, mpModel->getRadius() / 750.0f); - mFarZ = Radius * 10; - } -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - ModelViewer::UniquePtr pRenderer = std::make_unique(); - - SampleConfig config; - config.windowDesc.title = "Falcor Model Viewer"; - config.windowDesc.resizableWindow = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} \ No newline at end of file diff --git a/Samples/Utils/SceneEditor/Data/SceneEditorApp.slang b/Samples/Utils/SceneEditor/Data/SceneEditorApp.slang deleted file mode 100644 index b311be9fc..000000000 --- a/Samples/Utils/SceneEditor/Data/SceneEditorApp.slang +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -__import ShaderCommon; -__import Shading; -__import DefaultVS; - -float4 ps(VertexOut vOut) : SV_TARGET -{ - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); - - float3 color = 0; - - for (uint l = 0; l < gLightsCount; l++) - { - color += evalMaterial(sd, gLights[l], 1).color.rgb; - } - - return float4(color, 1.f); -} diff --git a/Samples/Utils/SceneEditor/SceneEditor.vcxproj b/Samples/Utils/SceneEditor/SceneEditor.vcxproj deleted file mode 100644 index 8d4a89a07..000000000 --- a/Samples/Utils/SceneEditor/SceneEditor.vcxproj +++ /dev/null @@ -1,96 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - - - - - - - - - {DE6A0005-923E-4007-B58C-3C35F690773F} - Win32Proj - SceneEditor - 10.0.17763.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/Samples/Utils/SceneEditor/SceneEditor.vcxproj.filters b/Samples/Utils/SceneEditor/SceneEditor.vcxproj.filters deleted file mode 100644 index 1c9696094..000000000 --- a/Samples/Utils/SceneEditor/SceneEditor.vcxproj.filters +++ /dev/null @@ -1,19 +0,0 @@ - - - - - {253f1672-d199-4da8-8930-e56acd084893} - - - - - - - - - - - Data - - - \ No newline at end of file diff --git a/Samples/Utils/SceneEditor/SceneEditorApp.cpp b/Samples/Utils/SceneEditor/SceneEditorApp.cpp deleted file mode 100644 index 1b45ec336..000000000 --- a/Samples/Utils/SceneEditor/SceneEditorApp.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#include "SceneEditorApp.h" -#include "Graphics/Scene/SceneImporter.h" - -void SceneEditorApp::onGuiRender(SampleCallbacks* pSample, Gui* pGui) -{ - pGui->addSeparator(); - - if (pGui->addButton("Create New Scene")) - { - createScene(); - } - if (pGui->addButton("Load Scene")) - { - loadScene(); - } - - if(mpEditor) - { - mpEditor->renderGui(pGui); - if(mpScene->getCameraCount()) - { - pGui->addCheckBox("Preview Camera", mCameraLiveViewMode); - } - } -} - -void SceneEditorApp::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) -{ - if (mpEditor) - { - mpEditor->onResizeSwapChain(); - } -} - -void SceneEditorApp::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ -} - -void SceneEditorApp::reset() -{ - mpProgram = nullptr; - mpVars = nullptr; -} - -void SceneEditorApp::initNewScene() -{ - if(mpScene) - { - mpRenderer = SceneRenderer::create(mpScene); - mpEditor = SceneEditor::create(mpScene); - - initShader(); - } -} - -void SceneEditorApp::initShader() -{ - mpProgram = GraphicsProgram::createFromFile("SceneEditorApp.slang", "", "ps"); - mpVars = GraphicsVars::create(mpProgram->getReflector()); -} - -void SceneEditorApp::loadScene() -{ - std::string Filename; - if(openFileDialog(Scene::kFileExtensionFilters, Filename)) - { - reset(); - - mpScene = Scene::loadFromFile(Filename, Model::LoadFlags::None, Scene::LoadFlags::None); - initNewScene(); - } -} - -void SceneEditorApp::createScene() -{ - reset(); - mpScene = Scene::create(); - initNewScene(); -} - -void SceneEditorApp::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) -{ - const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); - pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - - if(mpScene) - { - GraphicsState::SharedPtr pState = pRenderContext->getGraphicsState(); - pState->setBlendState(nullptr); - pState->setDepthStencilState(nullptr); - pRenderContext->setGraphicsVars(mpVars); - pState->setProgram(mpProgram); - - auto currentTime = pSample->getCurrentTime(); - mpEditor->update(currentTime); - mpRenderer->update(currentTime); - - const auto& pCamera = mCameraLiveViewMode ? mpScene->getActiveCamera() : mpEditor->getEditorCamera(); - mpRenderer->renderScene(pRenderContext, pCamera.get()); - } - - if (mpEditor && mCameraLiveViewMode == false) - { - mpEditor->render(pRenderContext); - } -} - -void SceneEditorApp::onShutdown(SampleCallbacks* pSample) -{ - reset(); -} - -bool SceneEditorApp::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) -{ - if (mCameraLiveViewMode) - { - return mpRenderer->onKeyEvent(keyEvent); - } - - return mpEditor ? mpEditor->onKeyEvent(keyEvent) : false; -} - -bool SceneEditorApp::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) -{ - if (mCameraLiveViewMode) - { - return mpRenderer->onMouseEvent(mouseEvent); - } - - return mpEditor ? mpEditor->onMouseEvent(pSample->getRenderContext(), mouseEvent) : false; -} - -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) -#endif -{ - SceneEditorApp::UniquePtr pRenderer = std::make_unique(); - SampleConfig config; - config.windowDesc.title = "Scene Editor"; - config.freezeTimeOnStartup = true; -#ifdef _WIN32 - Sample::run(config, pRenderer); -#else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); -#endif - return 0; -} diff --git a/Samples/Utils/SceneEditor/SceneEditorApp.h b/Samples/Utils/SceneEditor/SceneEditorApp.h deleted file mode 100644 index 66309933e..000000000 --- a/Samples/Utils/SceneEditor/SceneEditorApp.h +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of NVIDIA CORPORATION nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************************************************************************/ -#pragma once -#include "Falcor.h" - -using namespace Falcor; - -class SceneEditorApp: public Renderer -{ -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - void onShutdown(SampleCallbacks* pSample); - -private: - void loadScene(); - void createScene(); - void reset(); - void initNewScene(); - void initShader(); - - bool mCameraLiveViewMode = false; - - Scene::SharedPtr mpScene = nullptr; - GraphicsProgram::SharedPtr mpProgram = nullptr; - SceneRenderer::SharedPtr mpRenderer = nullptr; - SceneEditor::UniquePtr mpEditor = nullptr; - GraphicsVars::SharedPtr mpVars = nullptr; -}; diff --git a/Framework/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp b/Source/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp similarity index 99% rename from Framework/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp rename to Source/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp index 2a400cf77..6b64d08db 100644 --- a/Framework/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp +++ b/Source/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.cpp @@ -2,10 +2,10 @@ #ifdef _WIN32 #include #endif -#include "Externals/dear_imgui/imgui.h" +#include "dear_imgui/imgui.h" #undef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS -#include "Externals/dear_imgui/imgui_internal.h" +#include "dear_imgui/imgui_internal.h" //----------------------------------------------------------------------------------------------------------------- #include // qsort @@ -927,7 +927,7 @@ namespace ImGui { bool nodeInEditMode = false; ImGui::BeginGroup(); // Lock horizontal position - ImGui::SetNextTreeNodeOpen(node->isOpen, ImGuiCond_Always); + ImGui::SetNextItemOpen(node->isOpen, ImGuiCond_Always); ImU32 titleTextColorU32 = 0, titleBgColorU32 = 0; float titleBgGradient = -1.f; node->getDefaultTitleBarColors(titleTextColorU32, titleBgColorU32, titleBgGradient); diff --git a/Framework/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h b/Source/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h similarity index 100% rename from Framework/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h rename to Source/Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h diff --git a/Source/Externals/mikktspace/README.md b/Source/Externals/mikktspace/README.md new file mode 100644 index 000000000..7704b7edc --- /dev/null +++ b/Source/Externals/mikktspace/README.md @@ -0,0 +1,5 @@ +# mikktspace + +This is a copy of [Morten S. Mikkelsen](http://mmikkelsen3d.blogspot.fr/)'s tangent space code written during his master thesis. + +More info on [wiki.blender](http://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps). diff --git a/Source/Externals/mikktspace/mikktspace.c b/Source/Externals/mikktspace/mikktspace.c new file mode 100644 index 000000000..2b952f4ac --- /dev/null +++ b/Source/Externals/mikktspace/mikktspace.c @@ -0,0 +1,1890 @@ +/** \file mikktspace/mikktspace.c + * \ingroup mikktspace + */ +/** + * Copyright (C) 2011 by Morten S. Mikkelsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include +#include + +#include "mikktspace.h" + +#define TFALSE 0 +#define TTRUE 1 + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#define INTERNAL_RND_SORT_SEED 39871946 + +// internal structure +typedef struct { + float x, y, z; +} SVec3; + +static tbool veq( const SVec3 v1, const SVec3 v2 ) +{ + return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); +} + +static SVec3 vadd( const SVec3 v1, const SVec3 v2 ) +{ + SVec3 vRes; + + vRes.x = v1.x + v2.x; + vRes.y = v1.y + v2.y; + vRes.z = v1.z + v2.z; + + return vRes; +} + + +static SVec3 vsub( const SVec3 v1, const SVec3 v2 ) +{ + SVec3 vRes; + + vRes.x = v1.x - v2.x; + vRes.y = v1.y - v2.y; + vRes.z = v1.z - v2.z; + + return vRes; +} + +static SVec3 vscale(const float fS, const SVec3 v) +{ + SVec3 vRes; + + vRes.x = fS * v.x; + vRes.y = fS * v.y; + vRes.z = fS * v.z; + + return vRes; +} + +static float LengthSquared( const SVec3 v ) +{ + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +static float Length( const SVec3 v ) +{ + return sqrtf(LengthSquared(v)); +} + +static SVec3 Normalize( const SVec3 v ) +{ + return vscale(1 / Length(v), v); +} + +static float vdot( const SVec3 v1, const SVec3 v2) +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + + +static tbool NotZero(const float fX) +{ + // could possibly use FLT_EPSILON instead + return fabsf(fX) > FLT_MIN; +} + +static tbool VNotZero(const SVec3 v) +{ + // might change this to an epsilon based test + return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); +} + + + +typedef struct { + int iNrFaces; + int * pTriMembers; +} SSubGroup; + +typedef struct { + int iNrFaces; + int * pFaceIndices; + int iVertexRepresentitive; + tbool bOrientPreservering; +} SGroup; + +// +#define MARK_DEGENERATE 1 +#define QUAD_ONE_DEGEN_TRI 2 +#define GROUP_WITH_ANY 4 +#define ORIENT_PRESERVING 8 + + + +typedef struct { + int FaceNeighbors[3]; + SGroup * AssignedGroup[3]; + + // normalized first order face derivatives + SVec3 vOs, vOt; + float fMagS, fMagT; // original magnitudes + + // determines if the current and the next triangle are a quad. + int iOrgFaceNumber; + int iFlag, iTSpacesOffs; + unsigned char vert_num[4]; +} STriInfo; + +typedef struct { + SVec3 vOs; + float fMagS; + SVec3 vOt; + float fMagT; + int iCounter; // this is to average back into quads. + tbool bOrient; +} STSpace; + +static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn); +static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], + const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, + const SMikkTSpaceContext * pContext); + +static int MakeIndex(const int iFace, const int iVert) +{ + assert(iVert>=0 && iVert<4 && iFace>=0); + return (iFace<<2) | (iVert&0x3); +} + +static void IndexToData(int * piFace, int * piVert, const int iIndexIn) +{ + piVert[0] = iIndexIn&0x3; + piFace[0] = iIndexIn>>2; +} + +static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1) +{ + STSpace ts_res; + + // this if is important. Due to floating point precision + // averaging when ts0==ts1 will cause a slight difference + // which results in tangent space splits later on + if (pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT && + veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt)) + { + ts_res.fMagS = pTS0->fMagS; + ts_res.fMagT = pTS0->fMagT; + ts_res.vOs = pTS0->vOs; + ts_res.vOt = pTS0->vOt; + } + else + { + ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS); + ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT); + ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs); + ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt); + if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs); + if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt); + } + + return ts_res; +} + + + +static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index); +static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index); +static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index); + + +// degen triangles +static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris); +static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris); + + +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext) +{ + return genTangSpace(pContext, 180.0f); +} + +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold) +{ + // count nr_triangles + int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL; + STriInfo * pTriInfos = NULL; + SGroup * pGroups = NULL; + STSpace * psTspace = NULL; + int iNrTrianglesIn = 0, f=0, t=0, i=0; + int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0; + int iNrActiveGroups = 0, index = 0; + const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); + tbool bRes = TFALSE; + const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f); + + // verify all call-backs have been set + if ( pContext->m_pInterface->m_getNumFaces==NULL || + pContext->m_pInterface->m_getNumVerticesOfFace==NULL || + pContext->m_pInterface->m_getPosition==NULL || + pContext->m_pInterface->m_getNormal==NULL || + pContext->m_pInterface->m_getTexCoord==NULL ) + return TFALSE; + + // count triangles on supported faces + for (f=0; fm_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts==3) ++iNrTrianglesIn; + else if (verts==4) iNrTrianglesIn += 2; + } + if (iNrTrianglesIn<=0) return TFALSE; + + // allocate memory for an index list + piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn); + pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn); + if (piTriListIn==NULL || pTriInfos==NULL) + { + if (piTriListIn!=NULL) free(piTriListIn); + if (pTriInfos!=NULL) free(pTriInfos); + return TFALSE; + } + + // make an initial triangle --> face index list + iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); + + // make a welded index list of identical positions and attributes (pos, norm, texc) + //printf("gen welded index list begin\n"); + GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn); + //printf("gen welded index list end\n"); + + // Mark all degenerate triangles + iTotTris = iNrTrianglesIn; + iDegenTriangles = 0; + for (t=0; tm_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts!=3 && verts!=4) continue; + + + // I've decided to let degenerate triangles and group-with-anythings + // vary between left/right hand coordinate systems at the vertices. + // All healthy triangles on the other hand are built to always be either or. + + /*// force the coordinate system orientation to be uniform for every face. + // (this is already the case for good triangles but not for + // degenerate ones and those with bGroupWithAnything==true) + bool bOrient = psTspace[index].bOrient; + if (psTspace[index].iCounter == 0) // tspace was not derived from a group + { + // look for a space created in GenerateTSpaces() by iCounter>0 + bool bNotFound = true; + int i=1; + while (i 0) bNotFound=false; + else ++i; + } + if (!bNotFound) bOrient = psTspace[index+i].bOrient; + }*/ + + // set data + for (i=0; ivOs.x, pTSpace->vOs.y, pTSpace->vOs.z}; + float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z}; + if (pContext->m_pInterface->m_setTSpace!=NULL) + pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i); + if (pContext->m_pInterface->m_setTSpaceBasic!=NULL) + pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i); + + ++index; + } + } + + free(psTspace); + + + return TTRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct { + float vert[3]; + int index; +} STmpVert; + +static const int g_iCells = 2048; + +#ifdef _MSC_VER + #define NOINLINE __declspec(noinline) +#else + #define NOINLINE __attribute__ ((noinline)) +#endif + +// it is IMPORTANT that this function is called to evaluate the hash since +// inlining could potentially reorder instructions and generate different +// results for the same effective input value fVal. +static NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal) +{ + const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin)); + const int iIndex = (int)fIndex; + return iIndex < g_iCells ? (iIndex >= 0 ? iIndex : 0) : (g_iCells - 1); +} + +static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in); +static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries); +static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); + +static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + + // Generate bounding box + int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL; + STmpVert * pTmpVert = NULL; + int i=0, iChannel=0, k=0, e=0; + int iMaxCount=0; + SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim; + float fMin, fMax; + for (i=1; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + + const SVec3 vP = GetPosition(pContext, index); + if (vMin.x > vP.x) vMin.x = vP.x; + else if (vMax.x < vP.x) vMax.x = vP.x; + if (vMin.y > vP.y) vMin.y = vP.y; + else if (vMax.y < vP.y) vMax.y = vP.y; + if (vMin.z > vP.z) vMin.z = vP.z; + else if (vMax.z < vP.z) vMax.z = vP.z; + } + + vDim = vsub(vMax,vMin); + iChannel = 0; + fMin = vMin.x; fMax=vMax.x; + if (vDim.y>vDim.x && vDim.y>vDim.z) + { + iChannel=1; + fMin = vMin.y, fMax=vMax.y; + } + else if (vDim.z>vDim.x) + { + iChannel=2; + fMin = vMin.z, fMax=vMax.z; + } + + // make allocations + piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); + piHashCount = (int *) malloc(sizeof(int)*g_iCells); + piHashOffsets = (int *) malloc(sizeof(int)*g_iCells); + piHashCount2 = (int *) malloc(sizeof(int)*g_iCells); + + if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL) + { + if (piHashTable!=NULL) free(piHashTable); + if (piHashCount!=NULL) free(piHashCount); + if (piHashOffsets!=NULL) free(piHashOffsets); + if (piHashCount2!=NULL) free(piHashCount2); + GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); + return; + } + memset(piHashCount, 0, sizeof(int)*g_iCells); + memset(piHashCount2, 0, sizeof(int)*g_iCells); + + // count amount of elements in each cell unit + for (i=0; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); + const int iCell = FindGridCell(fMin, fMax, fVal); + ++piHashCount[iCell]; + } + + // evaluate start index of each cell. + piHashOffsets[0]=0; + for (k=1; kpTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c]; + else if (fvMax[c]dx && dy>dz) channel=1; + else if (dz>dx) channel=2; + + fSep = 0.5f*(fvMax[channel]+fvMin[channel]); + + // terminate recursion when the separation/average value + // is no longer strictly between fMin and fMax values. + if (fSep>=fvMax[channel] || fSep<=fvMin[channel]) + { + // complete the weld + for (l=iL_in; l<=iR_in; l++) + { + int i = pTmpVert[l].index; + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const SVec3 vN = GetNormal(pContext, index); + const SVec3 vT = GetTexCoord(pContext, index); + + tbool bNotFound = TTRUE; + int l2=iL_in, i2rec=-1; + while (l20); // at least 2 entries + + // separate (by fSep) all points between iL_in and iR_in in pTmpVert[] + while (iL < iR) + { + tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE; + while ((!bReadyLeftSwap) && iL=iL_in && iL<=iR_in); + bReadyLeftSwap = !(pTmpVert[iL].vert[channel]=iL_in && iR<=iR_in); + bReadyRightSwap = pTmpVert[iR].vert[channel]m_pInterface->m_getNumFaces(pContext); f++) + { + const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts!=3 && verts!=4) continue; + + pTriInfos[iDstTriIndex].iOrgFaceNumber = f; + pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs; + + if (verts==3) + { + unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num; + pVerts[0]=0; pVerts[1]=1; pVerts[2]=2; + piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0); + piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1); + piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2); + ++iDstTriIndex; // next + } + else + { + { + pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f; + pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs; + } + + { + // need an order independent way to evaluate + // tspace on quads. This is done by splitting + // along the shortest diagonal. + const int i0 = MakeIndex(f, 0); + const int i1 = MakeIndex(f, 1); + const int i2 = MakeIndex(f, 2); + const int i3 = MakeIndex(f, 3); + const SVec3 T0 = GetTexCoord(pContext, i0); + const SVec3 T1 = GetTexCoord(pContext, i1); + const SVec3 T2 = GetTexCoord(pContext, i2); + const SVec3 T3 = GetTexCoord(pContext, i3); + const float distSQ_02 = LengthSquared(vsub(T2,T0)); + const float distSQ_13 = LengthSquared(vsub(T3,T1)); + tbool bQuadDiagIs_02; + if (distSQ_02m_pInterface->m_getPosition(pContext, pos, iF, iI); + res.x=pos[0]; res.y=pos[1]; res.z=pos[2]; + return res; +} + +static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float norm[3]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI); + res.x=norm[0]; res.y=norm[1]; res.z=norm[2]; + return res; +} + +static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float texc[2]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI); + res.x=texc[0]; res.y=texc[1]; res.z=1.0f; + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef union { + struct + { + int i0, i1, f; + }; + int array[3]; +} SEdge; + +static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn); +static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn); + +// returns the texture area times 2 +static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[]) +{ + const SVec3 t1 = GetTexCoord(pContext, indices[0]); + const SVec3 t2 = GetTexCoord(pContext, indices[1]); + const SVec3 t3 = GetTexCoord(pContext, indices[2]); + + const float t21x = t2.x-t1.x; + const float t21y = t2.y-t1.y; + const float t31x = t3.x-t1.x; + const float t31y = t3.y-t1.y; + + const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; + + return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2; +} + +static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + int f=0, i=0, t=0; + // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function. + + // generate neighbor info list + for (f=0; f0 ? ORIENT_PRESERVING : 0); + + if ( NotZero(fSignedAreaSTx2) ) + { + const float fAbsArea = fabsf(fSignedAreaSTx2); + const float fLenOs = Length(vOs); + const float fLenOt = Length(vOt); + const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f; + if ( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs); + if ( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt); + + // evaluate magnitudes prior to normalization of vOs and vOt + pTriInfos[f].fMagS = fLenOs / fAbsArea; + pTriInfos[f].fMagT = fLenOt / fAbsArea; + + // if this is a good triangle + if ( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) + pTriInfos[f].iFlag &= (~GROUP_WITH_ANY); + } + } + + // force otherwise healthy quads to a fixed orientation + while (t<(iNrTrianglesIn-1)) + { + const int iFO_a = pTriInfos[t].iOrgFaceNumber; + const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; + if (iFO_a==iFO_b) // this is a quad + { + const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + + // bad triangles should already have been removed by + // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false + if ((bIsDeg_a||bIsDeg_b)==TFALSE) + { + const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + // if this happens the quad has extremely bad mapping!! + if (bOrientA!=bOrientB) + { + //printf("found quad with bad mapping\n"); + tbool bChooseOrientFirstTri = TFALSE; + if ((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE; + else if ( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) ) + bChooseOrientFirstTri = TTRUE; + + // force match + { + const int t0 = bChooseOrientFirstTri ? t : (t+1); + const int t1 = bChooseOrientFirstTri ? (t+1) : t; + pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first + pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit + } + } + } + t += 2; + } + else + ++t; + } + + // match up edge pairs + { + SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3); + if (pEdges==NULL) + BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn); + else + { + BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn); + + free(pEdges); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup); +static void AddTriToGroup(SGroup * pGroup, const int iTriIndex); + +static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn) +{ + const int iNrMaxGroups = iNrTrianglesIn*3; + int iNrActiveGroups = 0; + int iOffset = 0, f=0, i=0; + (void)iNrMaxGroups; /* quiet warnings in non debug mode */ + for (f=0; fiVertexRepresentitive = vert_index; + pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0; + pTriInfos[f].AssignedGroup[i]->iNrFaces = 0; + pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset]; + ++iNrActiveGroups; + + AddTriToGroup(pTriInfos[f].AssignedGroup[i], f); + bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + neigh_indexL = pTriInfos[f].FaceNeighbors[i]; + neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2]; + if (neigh_indexL>=0) // neighbor + { + const tbool bAnswer = + AssignRecur(piTriListIn, pTriInfos, neigh_indexL, + pTriInfos[f].AssignedGroup[i] ); + + const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; + assert(bAnswer || bDiff); + (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ + } + if (neigh_indexR>=0) // neighbor + { + const tbool bAnswer = + AssignRecur(piTriListIn, pTriInfos, neigh_indexR, + pTriInfos[f].AssignedGroup[i] ); + + const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; + assert(bAnswer || bDiff); + (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ + } + + // update offset + iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces; + // since the groups are disjoint a triangle can never + // belong to more than 3 groups. Subsequently something + // is completely screwed if this assertion ever hits. + assert(iOffset <= iNrMaxGroups); + } + } + } + + return iNrActiveGroups; +} + +static void AddTriToGroup(SGroup * pGroup, const int iTriIndex) +{ + pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex; + ++pGroup->iNrFaces; +} + +static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], + const int iMyTriIndex, SGroup * pGroup) +{ + STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex]; + + // track down vertex + const int iVertRep = pGroup->iVertexRepresentitive; + const int * pVerts = &piTriListIn[3*iMyTriIndex+0]; + int i=-1; + if (pVerts[0]==iVertRep) i=0; + else if (pVerts[1]==iVertRep) i=1; + else if (pVerts[2]==iVertRep) i=2; + assert(i>=0 && i<3); + + // early out + if (pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE; + else if (pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE; + if ((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0) + { + // first to group with a group-with-anything triangle + // determines it's orientation. + // This is the only existing order dependency in the code!! + if ( pMyTriInfo->AssignedGroup[0] == NULL && + pMyTriInfo->AssignedGroup[1] == NULL && + pMyTriInfo->AssignedGroup[2] == NULL ) + { + pMyTriInfo->iFlag &= (~ORIENT_PRESERVING); + pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0); + } + } + { + const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + if (bOrient != pGroup->bOrientPreservering) return TFALSE; + } + + AddTriToGroup(pGroup, iMyTriIndex); + pMyTriInfo->AssignedGroup[i] = pGroup; + + { + const int neigh_indexL = pMyTriInfo->FaceNeighbors[i]; + const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2]; + if (neigh_indexL>=0) + AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); + if (neigh_indexR>=0) + AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); + } + + + + return TTRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2); +static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed); +static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive); + +static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], + const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, + const SMikkTSpaceContext * pContext) +{ + STSpace * pSubGroupTspace = NULL; + SSubGroup * pUniSubGroups = NULL; + int * pTmpMembers = NULL; + int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0; + for (g=0; giNrFaces; i++) // triangles + { + const int f = pGroup->pFaceIndices[i]; // triangle number + int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0; + SSubGroup tmp_group; + tbool bFound; + SVec3 n, vOs, vOt; + if (pTriInfos[f].AssignedGroup[0]==pGroup) index=0; + else if (pTriInfos[f].AssignedGroup[1]==pGroup) index=1; + else if (pTriInfos[f].AssignedGroup[2]==pGroup) index=2; + assert(index>=0 && index<3); + + iVertIndex = piTriListIn[f*3+index]; + assert(iVertIndex==pGroup->iVertexRepresentitive); + + // is normalized already + n = GetNormal(pContext, iVertIndex); + + // project + vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); + vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); + if ( VNotZero(vOs) ) vOs = Normalize(vOs); + if ( VNotZero(vOt) ) vOt = Normalize(vOt); + + // original face number + iOF_1 = pTriInfos[f].iOrgFaceNumber; + + iMembers = 0; + for (j=0; jiNrFaces; j++) + { + const int t = pGroup->pFaceIndices[j]; // triangle number + const int iOF_2 = pTriInfos[t].iOrgFaceNumber; + + // project + SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n)); + SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n)); + if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2); + if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2); + + { + const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE; + // make sure triangles which belong to the same quad are joined. + const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE; + + const float fCosS = vdot(vOs,vOs2); + const float fCosT = vdot(vOt,vOt2); + + assert(f!=t || bSameOrgFace); // sanity check + if (bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos)) + pTmpMembers[iMembers++] = t; + } + } + + // sort pTmpMembers + tmp_group.iNrFaces = iMembers; + tmp_group.pTriMembers = pTmpMembers; + if (iMembers>1) + { + unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? + QuickSort(pTmpMembers, 0, iMembers-1, uSeed); + } + + // look for an existing match + bFound = TFALSE; + l=0; + while (liVertexRepresentitive); + ++iUniqueSubGroups; + } + + // output tspace + { + const int iOffs = pTriInfos[f].iTSpacesOffs; + const int iVert = pTriInfos[f].vert_num[index]; + STSpace * pTS_out = &psTspace[iOffs+iVert]; + assert(pTS_out->iCounter<2); + assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering); + if (pTS_out->iCounter==1) + { + *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]); + pTS_out->iCounter = 2; // update counter + pTS_out->bOrient = pGroup->bOrientPreservering; + } + else + { + assert(pTS_out->iCounter==0); + *pTS_out = pSubGroupTspace[l]; + pTS_out->iCounter = 1; // update counter + pTS_out->bOrient = pGroup->bOrientPreservering; + } + } + } + + // clean up and offset iUniqueTspaces + for (s=0; s=0 && i<3); + + // project + index = piTriListIn[3*f+i]; + n = GetNormal(pContext, index); + vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); + vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); + if ( VNotZero(vOs) ) vOs = Normalize(vOs); + if ( VNotZero(vOt) ) vOt = Normalize(vOt); + + i2 = piTriListIn[3*f + (i<2?(i+1):0)]; + i1 = piTriListIn[3*f + i]; + i0 = piTriListIn[3*f + (i>0?(i-1):2)]; + + p0 = GetPosition(pContext, i0); + p1 = GetPosition(pContext, i1); + p2 = GetPosition(pContext, i2); + v1 = vsub(p0,p1); + v2 = vsub(p2,p1); + + // project + v1 = vsub(v1, vscale(vdot(n,v1),n)); if ( VNotZero(v1) ) v1 = Normalize(v1); + v2 = vsub(v2, vscale(vdot(n,v2),n)); if ( VNotZero(v2) ) v2 = Normalize(v2); + + // weight contribution by the angle + // between the two edge vectors + fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos); + fAngle = (float) acos(fCos); + fMagS = pTriInfos[f].fMagS; + fMagT = pTriInfos[f].fMagT; + + res.vOs=vadd(res.vOs, vscale(fAngle,vOs)); + res.vOt=vadd(res.vOt,vscale(fAngle,vOt)); + res.fMagS+=(fAngle*fMagS); + res.fMagT+=(fAngle*fMagT); + fAngleSum += fAngle; + } + } + + // normalize + if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs); + if ( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt); + if (fAngleSum>0) + { + res.fMagS /= fAngleSum; + res.fMagT /= fAngleSum; + } + + return res; +} + +static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2) +{ + tbool bStillSame=TTRUE; + int i=0; + if (pg1->iNrFaces!=pg2->iNrFaces) return TFALSE; + while (iiNrFaces && bStillSame) + { + bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE; + if (bStillSame) ++i; + } + return bStillSame; +} + +static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed) +{ + int iL, iR, n, index, iMid, iTmp; + + // Random + unsigned int t=uSeed&31; + t=(uSeed<>(32-t)); + uSeed=uSeed+t+3; + // Random end + + iL=iLeft; iR=iRight; + n = (iR-iL)+1; + assert(n>=0); + index = (int) (uSeed%n); + + iMid=pSortBuffer[index + iL]; + + + do + { + while (pSortBuffer[iL] < iMid) + ++iL; + while (pSortBuffer[iR] > iMid) + --iR; + + if (iL <= iR) + { + iTmp = pSortBuffer[iL]; + pSortBuffer[iL] = pSortBuffer[iR]; + pSortBuffer[iR] = iTmp; + ++iL; --iR; + } + } + while (iL <= iR); + + if (iLeft < iR) + QuickSort(pSortBuffer, iLeft, iR, uSeed); + if (iL < iRight) + QuickSort(pSortBuffer, iL, iRight, uSeed); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed); +static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in); + +static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn) +{ + // build array of edges + unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? + int iEntries=0, iCurStartIndex=-1, f=0, i=0; + for (f=0; f pSortBuffer[iRight].array[channel]) + { + sTmp = pSortBuffer[iLeft]; + pSortBuffer[iLeft] = pSortBuffer[iRight]; + pSortBuffer[iRight] = sTmp; + } + return; + } + + // Random + t=uSeed&31; + t=(uSeed<>(32-t)); + uSeed=uSeed+t+3; + // Random end + + iL=iLeft, iR=iRight; + n = (iR-iL)+1; + assert(n>=0); + index = (int) (uSeed%n); + + iMid=pSortBuffer[index + iL].array[channel]; + + do + { + while (pSortBuffer[iL].array[channel] < iMid) + ++iL; + while (pSortBuffer[iR].array[channel] > iMid) + --iR; + + if (iL <= iR) + { + sTmp = pSortBuffer[iL]; + pSortBuffer[iL] = pSortBuffer[iR]; + pSortBuffer[iR] = sTmp; + ++iL; --iR; + } + } + while (iL <= iR); + + if (iLeft < iR) + QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); + if (iL < iRight) + QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); +} + +// resolve ordering and edge number +static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in) +{ + *edgenum_out = -1; + + // test if first index is on the edge + if (indices[0]==i0_in || indices[0]==i1_in) + { + // test if second index is on the edge + if (indices[1]==i0_in || indices[1]==i1_in) + { + edgenum_out[0]=0; // first edge + i0_out[0]=indices[0]; + i1_out[0]=indices[1]; + } + else + { + edgenum_out[0]=2; // third edge + i0_out[0]=indices[2]; + i1_out[0]=indices[0]; + } + } + else + { + // only second and third index is on the edge + edgenum_out[0]=1; // second edge + i0_out[0]=indices[1]; + i1_out[0]=indices[2]; + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Degenerate triangles //////////////////////////////////// + +static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris) +{ + int iNextGoodTriangleSearchIndex=-1; + tbool bStillFindingGoodOnes; + + // locate quads with only one good triangle + int t=0; + while (t<(iTotTris-1)) + { + const int iFO_a = pTriInfos[t].iOrgFaceNumber; + const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; + if (iFO_a==iFO_b) // this is a quad + { + const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + if ((bIsDeg_a^bIsDeg_b)!=0) + { + pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI; + pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI; + } + t += 2; + } + else + ++t; + } + + // reorder list so all degen triangles are moved to the back + // without reordering the good triangles + iNextGoodTriangleSearchIndex = 1; + t=0; + bStillFindingGoodOnes = TTRUE; + while (t (t+1)); + + // swap triangle t0 and t1 + if (!bJustADegenerate) + { + int i=0; + for (i=0; i<3; i++) + { + const int index = piTriList_out[t0*3+i]; + piTriList_out[t0*3+i] = piTriList_out[t1*3+i]; + piTriList_out[t1*3+i] = index; + } + { + const STriInfo tri_info = pTriInfos[t0]; + pTriInfos[t0] = pTriInfos[t1]; + pTriInfos[t1] = tri_info; + } + } + else + bStillFindingGoodOnes = TFALSE; // this is not supposed to happen + } + + if (bStillFindingGoodOnes) ++t; + } + + assert(bStillFindingGoodOnes); // code will still work. + assert(iNrTrianglesIn == t); +} + +static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris) +{ + int t=0, i=0; + // deal with degenerate triangles + // punishment for degenerate triangles is O(N^2) + for (t=iNrTrianglesIn; t http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf + * Note that though the tangent spaces at the vertices are generated in an order-independent way, + * by this implementation, the interpolated tangent space is still affected by which diagonal is + * chosen to split each quad. A sensible solution is to have your tools pipeline always + * split quads by the shortest diagonal. This choice is order-independent and works with mirroring. + * If these have the same length then compare the diagonals defined by the texture coordinates. + * XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin + * and also quad triangulator plugin. + */ + + +typedef int tbool; +typedef struct SMikkTSpaceContext SMikkTSpaceContext; + +typedef struct { + // Returns the number of faces (triangles/quads) on the mesh to be processed. + int (*m_getNumFaces)(const SMikkTSpaceContext * pContext); + + // Returns the number of vertices on face number iFace + // iFace is a number in the range {0, 1, ..., getNumFaces()-1} + int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace); + + // returns the position/normal/texcoord of the referenced face of vertex number iVert. + // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. + void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); + void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); + void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); + + // either (or both) of the two setTSpace callbacks can be set. + // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. + + // This function is used to return the tangent and fSign to the application. + // fvTangent is a unit length vector. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); + + // This function is used to return tangent space results to the application. + // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their + // true magnitudes which can be used for relief mapping effects. + // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. + // However, both are perpendicular to the vertex normal. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, + const tbool bIsOrientationPreserving, const int iFace, const int iVert); +} SMikkTSpaceInterface; + +struct SMikkTSpaceContext +{ + SMikkTSpaceInterface * m_pInterface; // initialized with callback functions + void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call) +}; + +// these are both thread safe! +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold); + + +// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the +// normal map sampler must use the exact inverse of the pixel shader transformation. +// The most efficient transformation we can possibly do in the pixel shader is +// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. +// pixel shader (fast transform out) +// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); +// where vNt is the tangent space normal. The normal map sampler must likewise use the +// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. +// sampler does (exact inverse of pixel shader): +// float3 row0 = cross(vB, vN); +// float3 row1 = cross(vN, vT); +// float3 row2 = cross(vT, vB); +// float fSign = dot(vT, row0)<0 ? -1 : 1; +// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); +// where vNout is the sampled normal in some chosen 3D space. +// +// Should you choose to reconstruct the bitangent in the pixel shader instead +// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. +// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of +// quads as your renderer then problems will occur since the interpolated tangent spaces will differ +// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before +// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. +// However, this must be used both by the sampler and your tools/rendering pipeline. + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/Externals/xoshiro/README.txt b/Source/Externals/xoshiro/README.txt new file mode 100644 index 000000000..e5ab4447a --- /dev/null +++ b/Source/Externals/xoshiro/README.txt @@ -0,0 +1,9 @@ +xoshiro128starstar.c + +Reference implementation of the xoshiro128** pseudorandom number generator. +Downloaded from http://xoshiro.di.unimi.it/xoshiro128starstar.c on 2018-11-20. + +splitmix64.c + +Reference implementation of the SplitMix64 pseudorandom number generator. +Downloaded from http://xoshiro.di.unimi.it/splitmix64.c on 2018-11-20. diff --git a/Source/Externals/xoshiro/splitmix64.c b/Source/Externals/xoshiro/splitmix64.c new file mode 100644 index 000000000..34a020134 --- /dev/null +++ b/Source/Externals/xoshiro/splitmix64.c @@ -0,0 +1,28 @@ +/* Written in 2015 by Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include + +/* This is a fixed-increment version of Java 8's SplittableRandom generator + See http://dx.doi.org/10.1145/2714064.2660195 and + http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html + + It is a very fast generator passing BigCrush, and it can be useful if + for some reason you absolutely want 64 bits of state; otherwise, we + rather suggest to use a xoroshiro128+ (for moderately parallel + computations) or xorshift1024* (for massively parallel computations) + generator. */ + +static uint64_t x; /* The state can be seeded with any value. */ + +uint64_t next() { + uint64_t z = (x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} diff --git a/Source/Externals/xoshiro/xoshiro128starstar.c b/Source/Externals/xoshiro/xoshiro128starstar.c new file mode 100644 index 000000000..0bd7cc850 --- /dev/null +++ b/Source/Externals/xoshiro/xoshiro128starstar.c @@ -0,0 +1,72 @@ +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +#include + +/* This is xoshiro128** 1.0, our 32-bit all-purpose, rock-solid generator. It + has excellent (sub-ns) speed, a state size (128 bits) that is large + enough for mild parallelism, and it passes all tests we are aware of. + + For generating just single-precision (i.e., 32-bit) floating-point + numbers, xoshiro128+ is even faster. + + The state must be seeded so that it is not everywhere zero. */ + + +static inline uint32_t rotl(const uint32_t x, int k) { + return (x << k) | (x >> (32 - k)); +} + + +static uint32_t s[4]; + +uint32_t next(void) { + const uint32_t result_starstar = rotl(s[0] * 5, 7) * 9; + + const uint32_t t = s[1] << 9; + + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + + s[2] ^= t; + + s[3] = rotl(s[3], 11); + + return result_starstar; +} + + +/* This is the jump function for the generator. It is equivalent + to 2^64 calls to next(); it can be used to generate 2^64 + non-overlapping subsequences for parallel computations. */ + +void jump(void) { + static const uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }; + + uint32_t s0 = 0; + uint32_t s1 = 0; + uint32_t s2 = 0; + uint32_t s3 = 0; + for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) + for(int b = 0; b < 32; b++) { + if (JUMP[i] & UINT32_C(1) << b) { + s0 ^= s[0]; + s1 ^= s[1]; + s2 ^= s[2]; + s3 ^= s[3]; + } + next(); + } + + s[0] = s0; + s[1] = s1; + s[2] = s2; + s[3] = s3; +} diff --git a/Framework/Source/API/BlendState.cpp b/Source/Falcor/Core/API/BlendState.cpp similarity index 94% rename from Framework/Source/API/BlendState.cpp rename to Source/Falcor/Core/API/BlendState.cpp index 7fd8763e2..f7deb1fdb 100644 --- a/Framework/Source/API/BlendState.cpp +++ b/Source/Falcor/Core/API/BlendState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,13 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/BlendState.h" -#include "API/FBO.h" +#include "stdafx.h" +#include "BlendState.h" +#include "FBO.h" namespace Falcor { - BlendState::SharedPtr BlendState::create(const Desc& desc) { return SharedPtr(new BlendState(desc)); @@ -73,4 +72,9 @@ namespace Falcor mRtDesc[rtIndex].writeMask.writeAlpha = writeAlpha; return *this; } + + SCRIPT_BINDING(BlendState) + { + m.regClass(BlendState); + } } diff --git a/Framework/Source/API/BlendState.h b/Source/Falcor/Core/API/BlendState.h similarity index 98% rename from Framework/Source/API/BlendState.h rename to Source/Falcor/Core/API/BlendState.h index 6e7b4c842..ce4b789a4 100644 --- a/Framework/Source/API/BlendState.h +++ b/Source/Falcor/Core/API/BlendState.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,13 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec4.hpp" namespace Falcor { /** Blend state */ - class BlendState : public std::enable_shared_from_this + class dlldecl BlendState : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -74,7 +73,7 @@ namespace Falcor /** Descriptor used to create new blend-state */ - class Desc + class dlldecl Desc { public: Desc(); diff --git a/Source/Falcor/Core/API/Buffer.cpp b/Source/Falcor/Core/API/Buffer.cpp new file mode 100644 index 000000000..7c0ebd118 --- /dev/null +++ b/Source/Falcor/Core/API/Buffer.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Buffer.h" +#include "Device.h" + +namespace Falcor +{ + size_t getBufferDataAlignment(const Buffer* pBuffer); + void* mapBufferApi(const Buffer::ApiHandle& apiHandle, size_t size); + + Buffer::SharedPtr Buffer::create(size_t size, BindFlags usage, CpuAccess cpuAccess, const void* pInitData) + { + Buffer::SharedPtr pBuffer = SharedPtr(new Buffer(size, usage, cpuAccess)); + if (pBuffer->apiInit(pInitData != nullptr)) + { + if (pInitData) pBuffer->setBlob(pInitData, 0, size); + return pBuffer; + } + else return nullptr; + } + + Buffer::SharedPtr Buffer::aliasResource(Resource::SharedPtr pBaseResource, GpuAddress offset, size_t size, Resource::BindFlags bindFlags) + { + assert(pBaseResource->asBuffer()); // Only aliasing buffers for now + CpuAccess cpuAccess = pBaseResource->asBuffer() ? pBaseResource->asBuffer()->getCpuAccess() : CpuAccess::None; + if (cpuAccess != CpuAccess::None) + { + logError("Buffer::aliasResource() - trying to alias a buffer with CpuAccess::" + to_string(cpuAccess) + " which is illegal. Aliased resource must have CpuAccess::None"); + return nullptr; + } + + if ((pBaseResource->getBindFlags() & bindFlags) != bindFlags) + { + logError("Buffer::aliasResource() - requested buffer bind-flags don't match the aliased resource bind flags.\nRequested = " + to_string(bindFlags) + "\nAliased = " + to_string(pBaseResource->getBindFlags())); + return nullptr; + } + + if (offset >= pBaseResource->getSize() || (offset + size) >= pBaseResource->getSize()) + { + logError("Buffer::aliasResource() - requested offset and size don't fit inside the alias resource dimensions. Requesed size = " + + to_string(size) + ", offset = " + to_string(offset) + ". Aliased resource size = " + to_string(pBaseResource->getSize())); + return nullptr; + } + + SharedPtr pBuffer = SharedPtr(new Buffer(size, bindFlags, CpuAccess::None)); + pBuffer->mpAliasedResource = pBaseResource; + pBuffer->mApiHandle = pBaseResource->getApiHandle(); + pBuffer->mGpuVaOffset = offset; + return pBuffer; + } + + Buffer::SharedPtr Buffer::createFromApiHandle(ApiHandle handle, size_t size, Resource::BindFlags usage, CpuAccess cpuAccess) + { + Buffer::SharedPtr pBuffer = SharedPtr(new Buffer(size, usage, cpuAccess)); + pBuffer->mApiHandle = handle; + return pBuffer->mApiHandle ? pBuffer : nullptr; + } + + Buffer::~Buffer() + { + if (mpAliasedResource) return; + + if (mDynamicData.pResourceHandle) + { + gpDevice->getUploadHeap()->release(mDynamicData); + } + else + { + gpDevice->releaseResource(mApiHandle); + } + } + + template + using CreateFuncType = std::function; + + template + typename ViewClass::SharedPtr findViewCommon(Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount, ViewMapType& viewMap, CreateFuncType createFunc) + { + ResourceViewInfo view = ResourceViewInfo(firstElement, elementCount); + + if (viewMap.find(view) == viewMap.end()) + { + viewMap[view] = createFunc(pBuffer, firstElement, elementCount); + } + + return viewMap[view]; + } + + ShaderResourceView::SharedPtr Buffer::getSRV(uint32_t firstElement, uint32_t elementCount) + { + auto createFunc = [](Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) + { + return ShaderResourceView::create(pBuffer->shared_from_this(), firstElement, elementCount); + }; + + return findViewCommon(this, firstElement, elementCount, mSrvs, createFunc); + } + + UnorderedAccessView::SharedPtr Buffer::getUAV(uint32_t firstElement, uint32_t elementCount) + { + auto createFunc = [](Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) + { + return UnorderedAccessView::create(pBuffer->shared_from_this(), firstElement, elementCount); + }; + + return findViewCommon(this, firstElement, elementCount, mUavs, createFunc); + } + + bool Buffer::setBlob(const void* pData, size_t offset, size_t size) + { + if (offset + size > mSize) + { + logError("Error when setting blob to buffer. Blob to large and will result in an overflow. Ignoring call"); + return false; + } + + if (mCpuAccess == CpuAccess::Write) + { + uint8_t* pDst = (uint8_t*)map(MapType::WriteDiscard) + offset; + std::memcpy(pDst, pData, size); + } + else + { + gpDevice->getRenderContext()->updateBuffer(this, pData, offset, size); + } + return true; + } + + void Buffer::updateData(const void* pData, size_t offset, size_t size) + { + setBlob(pData, offset, size); + } + + void* Buffer::map(MapType type) + { + if (type == MapType::WriteDiscard) + { + if (mCpuAccess != CpuAccess::Write) + { + logError("Trying to map a buffer for write, but it wasn't created with the write permissions"); + return nullptr; + } + + // Allocate a new buffer + if (mDynamicData.pResourceHandle) + { + gpDevice->getUploadHeap()->release(mDynamicData); + } + mDynamicData = gpDevice->getUploadHeap()->allocate(mSize, getBufferDataAlignment(this)); + mApiHandle = mDynamicData.pResourceHandle; + mGpuVaOffset = mDynamicData.offset; + invalidateViews(); + return mDynamicData.pData; + } + else + { + assert(type == MapType::Read); + + if (mBindFlags == BindFlags::None) + { + return mapBufferApi(mApiHandle, mSize); + } + else + { + logWarning("Buffer::map() performance warning - using staging resource which require us to flush the pipeline and wait for the GPU to finish its work"); + if (mpStagingResource == nullptr) + { + mpStagingResource = Buffer::create(mSize, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); + } + + // Copy the buffer and flush the pipeline + RenderContext* pContext = gpDevice->getRenderContext(); + pContext->copyResource(mpStagingResource.get(), this); + pContext->flush(true); + return mpStagingResource->map(MapType::Read); + } + } + } + + SCRIPT_BINDING(Buffer) + { + m.regClass(Buffer); + } +} diff --git a/Framework/Source/API/Buffer.h b/Source/Falcor/Core/API/Buffer.h similarity index 67% rename from Framework/Source/API/Buffer.h rename to Source/Falcor/Core/API/Buffer.h index ef44f360d..9f22308f8 100644 --- a/Framework/Source/API/Buffer.h +++ b/Source/Falcor/Core/API/Buffer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,14 +27,14 @@ ***************************************************************************/ #pragma once #include "Resource.h" -#include "LowLevel/ResourceAllocator.h" +#include "GpuMemoryHeap.h" namespace Falcor { /** Low-level buffer object This class abstracts the API's buffer creation and management */ - class Buffer : public Resource, public inherit_shared_from_this + class dlldecl Buffer : public Resource, public inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -52,17 +52,6 @@ namespace Falcor Read, ///< The buffer can be mapped for CPU reads }; - /** Buffer GPU access flags. - These flags are hints to the driver about how the buffer will - be used from the GPU. - */ - enum class GpuAccessFlags - { - ReadOnly = 0, ///< Buffer will mapped for GPU read only. - ReadWrite = 1, ///< Buffer will mapped for GPU read-write. - WriteOnly = 2, ///< Buffer will mapped for GPU write only. - }; - enum class MapType { Read, ///< Map the buffer for read access. Buffer had to be created with AccessFlags#MapWrite flag. @@ -79,6 +68,28 @@ namespace Falcor \return A pointer to a new buffer object, or nullptr if creation failed. */ static SharedPtr create(size_t size, Resource::BindFlags bind, CpuAccess cpuAccess, const void* pInitData = nullptr); + static SharedPtr aliasResource(Resource::SharedPtr pBaseResource, GpuAddress offset, size_t size, Resource::BindFlags bindFlags); + + /** Create a new buffer from an existing API handle + \param[in] ApiHandle Handle of already-allocated resource + \param[in] size The size of the buffer in bytes. + \param[in] bind Buffer bind flags. Flags must match the bind flags of the original resource. + \param[in] cpuAccess Flags indicating how the buffer can be updated. Flags must match those of the heap the original resource is allocated on. + \return A pointer to a new buffer object, or nullptr if creation failed. + */ + static SharedPtr createFromApiHandle(ApiHandle handle, size_t size, Resource::BindFlags bind, CpuAccess cpuAccess); + + /** Get a shader-resource view. + \param[in] firstElement The first element of the view. For raw buffers, an element is a single float + \param[in] elementCount The number of elements to bind + */ + ShaderResourceView::SharedPtr getSRV(uint32_t firstElement = 0, uint32_t elementCount = kMaxPossible); + + /** Get an unordered access view. + \param[in] firstElement The first element of the view. For raw buffers, an element is a single float + \param[in] elementCount The number of elements to bind + */ + UnorderedAccessView::SharedPtr getUAV(uint32_t firstElement = 0, uint32_t elementCount = kMaxPossible); /** Update the buffer's data \param[in] pData Pointer to the source data. @@ -86,11 +97,14 @@ namespace Falcor \param[in] size Number of bytes to copy. If offset and size will cause an out-of-bound access to the buffer, an error will be logged and the update will fail. */ + virtual bool setBlob(const void* pData, size_t offset, size_t size); + + deprecate("4.0", "Use setBlob() instead") void updateData(const void* pData, size_t offset, size_t size); /** Get the offset from the beginning of the GPU resource */ - uint64_t getGpuAddressOffset() const { return mDynamicData.offset; }; + uint64_t getGpuAddressOffset() const { return mGpuVaOffset; }; /** Get the GPU address (this includes the offset) */ @@ -108,15 +122,6 @@ namespace Falcor */ void unmap(); - /** Load the buffer to the GPU memory. - \return The GPU address, which can be used as a pointer in shaders. - */ - uint64_t makeResident(Buffer::GpuAccessFlags flags = Buffer::GpuAccessFlags::ReadOnly) const; - - /** Unload the texture to the GPU memory. This function is only valid after makeResident() call was made with a matching sample. If makeResident() wasn't called, the evict() will be silently ignored. - */ - void evict() const; - /** Get safe offset and size values */ bool adjustSizeOffsetParams(size_t& size, size_t& offset) const @@ -141,11 +146,40 @@ namespace Falcor protected: bool apiInit(bool hasInitData); - Buffer(size_t size, BindFlags bind, CpuAccess update) : Resource(Type::Buffer, bind), mSize(size), mCpuAccess(update){} + Buffer(size_t size, BindFlags bind, CpuAccess update) : Resource(Type::Buffer, bind, size), mCpuAccess(update){} - size_t mSize = 0; CpuAccess mCpuAccess; - ResourceAllocator::AllocationData mDynamicData; + GpuMemoryHeap::Allocation mDynamicData; Buffer::SharedPtr mpStagingResource; // For buffers that have both CPU read flag and can be used by the GPU + Resource::SharedPtr mpAliasedResource; }; -} \ No newline at end of file + + inline std::string to_string(Buffer::CpuAccess c) + { +#define a2s(ca_) case Buffer::CpuAccess::ca_: return #ca_; + switch (c) + { + a2s(None); + a2s(Write); + a2s(Read); + default: + should_not_get_here(); + return ""; + } +#undef a2s + } + + inline std::string to_string(Buffer::MapType mt) + { +#define t2s(t_) case Buffer::MapType::t_: return #t_; + switch (mt) + { + t2s(Read); + t2s(WriteDiscard); + default: + should_not_get_here(); + return ""; + } +#undef t2s + } +} diff --git a/Source/Falcor/Core/API/ComputeContext.cpp b/Source/Falcor/Core/API/ComputeContext.cpp new file mode 100644 index 000000000..31a95ae88 --- /dev/null +++ b/Source/Falcor/Core/API/ComputeContext.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ComputeContext.h" + +namespace Falcor +{ + ComputeContext::SharedPtr ComputeContext::create(CommandQueueHandle queue) + { + SharedPtr pCtx = SharedPtr(new ComputeContext()); + pCtx->mpLowLevelData = LowLevelContextData::create(LowLevelContextData::CommandQueueType::Compute, queue); + if (pCtx->mpLowLevelData == nullptr) return nullptr; + pCtx->bindDescriptorHeaps(); + return pCtx; + } + + bool ComputeContext::applyComputeVars(ComputeVars* pVars) + { + bool varsChanged = (pVars != mpLastBoundComputeVars); + + // FIXME TODO Temporary workaround + varsChanged = true; + + if (pVars->apply(this, varsChanged) == false) + { + logWarning("ComputeContext::applyComputeVars() - applying ComputeVars failed, most likely because we ran out of descriptors. Flushing the GPU and retrying"); + flush(true); + if (!pVars->apply(this, varsChanged)) + { + logError("ComputeVars::applyComputeVars() - applying ComputeVars failed, most likely because we ran out of descriptors"); + return false; + } + } + return true; + } + + void ComputeContext::flush(bool wait) + { + CopyContext::flush(wait); + mpLastBoundComputeVars = nullptr; + } +} diff --git a/Framework/Source/API/ComputeContext.h b/Source/Falcor/Core/API/ComputeContext.h similarity index 58% rename from Framework/Source/API/ComputeContext.h rename to Source/Falcor/Core/API/ComputeContext.h index c92705d7d..9aa5d660f 100644 --- a/Framework/Source/API/ComputeContext.h +++ b/Source/Falcor/Core/API/ComputeContext.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,12 +27,12 @@ ***************************************************************************/ #pragma once #include "CopyContext.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/ComputeState.h" +#include "Core/Program/ProgramVars.h" +#include "Core/State/ComputeState.h" namespace Falcor { - class ComputeContext : public CopyContext + class dlldecl ComputeContext : public CopyContext { public: using SharedPtr = std::shared_ptr; @@ -41,41 +41,14 @@ namespace Falcor static SharedPtr create(CommandQueueHandle queue); - /** Set the compute variables - */ - void setComputeVars(const ComputeVars::SharedPtr& pVars) { mBindComputeRootSig = mBindComputeRootSig || (mpComputeVars != pVars); mpComputeVars = pVars;} - - /** Get the bound program variables object - */ - ComputeVars::SharedPtr getComputeVars() const { return mpComputeVars; } - - /** Push the current compute vars and sets a new one - */ - void pushComputeVars(const ComputeVars::SharedPtr& pVars); - - /** Pops the last ProgramVars from the stack and sets it - */ - void popComputeVars(); - - /** Set a compute state - */ - void setComputeState(const ComputeState::SharedPtr& pState) { mpComputeState = pState; } - - /** Get the currently bound compute state - */ - ComputeState::SharedPtr getComputeState() const { return mpComputeState; } - - /** Push the current compute state and sets a new one - */ - void pushComputeState(const ComputeState::SharedPtr& pState); - - /** Pops the last PipelineState from the stack and sets it + /** Dispatch a compute task + \param[in] dispatchSize 3D dispatch group size */ - void popComputeState(); + void dispatch(ComputeState* pState, ComputeVars* pVars, const uvec3& dispatchSize); - /** Dispatch a compute task + /** Executes a dispatch call. Args to the dispatch call are contained in pArgBuffer */ - void dispatch(uint32_t groupSizeX, uint32_t groupSizeY, uint32_t groupSizeZ); + void dispatchIndirect(ComputeState* pState, ComputeVars* pVars, const Buffer* pArgBuffer, uint64_t argBufferOffset); /** Clear an unordered-access view \param[in] pUav The UAV to clear @@ -94,30 +67,16 @@ namespace Falcor \param[in] value Value to clear counter to */ void clearUAVCounter(const StructuredBuffer::SharedPtr& pBuffer, uint32_t value); - - /** Executes a dispatch call. Args to the dispatch call are contained in argbuffer - */ - void dispatchIndirect(const Buffer* argBuffer, uint64_t argBufferOffset); /** Submit the command list */ virtual void flush(bool wait = false) override; protected: ComputeContext(); - void prepareForDispatch(); - void applyComputeVars(); - - std::stack mpComputeStateStack; - std::stack mpComputeVarsStack; - bool mBindComputeRootSig = true; + bool prepareForDispatch(ComputeState* pState, ComputeVars* pVars); + bool applyComputeVars(ComputeVars* pVars); - ComputeVars::SharedPtr mpComputeVars; - ComputeState::SharedPtr mpComputeState; - - /** Initializes the command signature for calling dispatchIndirect - */ - static void initDispatchCommandSignature(); - static CommandSignatureHandle spDispatchCommandSig; + const ComputeVars* mpLastBoundComputeVars = nullptr; }; } diff --git a/Framework/Source/API/ComputeStateObject.cpp b/Source/Falcor/Core/API/ComputeStateObject.cpp similarity index 96% rename from Framework/Source/API/ComputeStateObject.cpp rename to Source/Falcor/Core/API/ComputeStateObject.cpp index bacfc3cae..dfe1cfb1a 100644 --- a/Framework/Source/API/ComputeStateObject.cpp +++ b/Source/Falcor/Core/API/ComputeStateObject.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ComputeStateObject.h" #include "Device.h" @@ -54,4 +54,4 @@ namespace Falcor } return pState; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/ComputeStateObject.h b/Source/Falcor/Core/API/ComputeStateObject.h similarity index 85% rename from Framework/Source/API/ComputeStateObject.h rename to Source/Falcor/Core/API/ComputeStateObject.h index e6daf5955..f7e18d807 100644 --- a/Framework/Source/API/ComputeStateObject.h +++ b/Source/Falcor/Core/API/ComputeStateObject.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,14 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Program/ProgramVersion.h" -#include "API/LowLevel/RootSignature.h" +#include "Core/Program/ProgramVersion.h" +#include "Core/API/RootSignature.h" namespace Falcor { - class RenderContext; - - class ComputeStateObject + class dlldecl ComputeStateObject { public: using SharedPtr = std::shared_ptr; @@ -42,12 +40,12 @@ namespace Falcor ~ComputeStateObject(); - class Desc + class dlldecl Desc { public: Desc& setRootSignature(RootSignature::SharedPtr pSignature) { mpRootSignature = pSignature; return *this; } - Desc& setProgramVersion(ProgramVersion::SharedConstPtr pProgram) { mpProgram = pProgram; return *this; } - ProgramVersion::SharedConstPtr getProgramVersion() const { return mpProgram; } + Desc& setProgramVersion(const ProgramVersion::SharedConstPtr& pProgram) { mpProgram = pProgram; return *this; } + const ProgramVersion::SharedConstPtr getProgramVersion() const { return mpProgram; } bool operator==(const Desc& other) const; private: friend class ComputeStateObject; @@ -64,4 +62,4 @@ namespace Falcor ApiHandle mApiHandle; bool apiInit(); }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/CopyContext.cpp b/Source/Falcor/Core/API/CopyContext.cpp similarity index 80% rename from Framework/Source/API/CopyContext.cpp rename to Source/Falcor/Core/API/CopyContext.cpp index 36b0d040f..98d4d34cd 100644 --- a/Framework/Source/API/CopyContext.cpp +++ b/Source/Falcor/Core/API/CopyContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/CopyContext.h" -#include "API/Device.h" -#include "API/Buffer.h" -#include +#include "stdafx.h" +#include "CopyContext.h" +#include "LowLevelContextData.h" +#include "Texture.h" +#include "Buffer.h" +#include "GpuFence.h" namespace Falcor { @@ -41,7 +42,7 @@ namespace Falcor pCtx->mpLowLevelData = LowLevelContextData::create(LowLevelContextData::CommandQueueType::Copy, queue); return pCtx->mpLowLevelData ? pCtx : nullptr; } - + void CopyContext::flush(bool wait) { if (mCommandsPending) @@ -49,7 +50,7 @@ namespace Falcor mpLowLevelData->flush(); mCommandsPending = false; } - else + else { // We need to signal even if there are no commands to execute. We need this because some resources may have been released since the last flush(), and unless we signal they will not be released mpLowLevelData->getFence()->gpuSignal(mpLowLevelData->getCommandQueue()); @@ -74,7 +75,7 @@ namespace Falcor return pTask->getData(); } - void CopyContext::resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo) + bool CopyContext::resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo) { const Texture* pTexture = dynamic_cast(pResource); if (pTexture) @@ -90,21 +91,21 @@ namespace Falcor if (globalBarrier) { - textureBarrier(pTexture, newState); + return textureBarrier(pTexture, newState); } else { - subresourceBarriers(pTexture, newState, pViewInfo); + return subresourceBarriers(pTexture, newState, pViewInfo); } } else { const Buffer* pBuffer = dynamic_cast(pResource); - bufferBarrier(pBuffer, newState); + return bufferBarrier(pBuffer, newState); } } - void CopyContext::subresourceBarriers(const Texture* pTexture, Resource::State newState, const ResourceViewInfo* pViewInfo) + bool CopyContext::subresourceBarriers(const Texture* pTexture, Resource::State newState, const ResourceViewInfo* pViewInfo) { ResourceViewInfo fullResource; bool setGlobal = false; @@ -118,6 +119,8 @@ namespace Falcor pViewInfo = &fullResource; } + bool entireViewTransitioned = true; + for (uint32_t a = pViewInfo->firstArraySlice; a < pViewInfo->firstArraySlice + pViewInfo->arraySize; a++) { for (uint32_t m = pViewInfo->mostDetailedMip; m < pViewInfo->mipCount + pViewInfo->mostDetailedMip; m++) @@ -129,9 +132,11 @@ namespace Falcor if (setGlobal == false) pTexture->setSubresourceState(a, m, newState); mCommandsPending = true; } + else entireViewTransitioned = false; } } if (setGlobal) pTexture->setGlobalState(newState); + return entireViewTransitioned; } void CopyContext::updateTextureData(const Texture* pTexture, const void* pData) @@ -150,4 +155,24 @@ namespace Falcor mCommandsPending = true; updateTextureSubresources(pDst, subresource, 1, pData, offset, size); } -} \ No newline at end of file + + void CopyContext::updateBuffer(const Buffer* pBuffer, const void* pData, size_t offset, size_t numBytes) + { + if (numBytes == 0) + { + numBytes = pBuffer->getSize() - offset; + } + + if (pBuffer->adjustSizeOffsetParams(numBytes, offset) == false) + { + logWarning("CopyContext::updateBuffer() - size and offset are invalid. Nothing to update."); + return; + } + + mCommandsPending = true; + // Allocate a buffer on the upload heap + Buffer::SharedPtr pUploadBuffer = Buffer::create(numBytes, Buffer::BindFlags::None, Buffer::CpuAccess::Write, pData); + + copyBufferRegion(pBuffer, offset, pUploadBuffer.get(), 0, numBytes); + } +} diff --git a/Framework/Source/API/CopyContext.h b/Source/Falcor/Core/API/CopyContext.h similarity index 90% rename from Framework/Source/API/CopyContext.h rename to Source/Falcor/Core/API/CopyContext.h index b76553622..055fd1d1e 100644 --- a/Framework/Source/API/CopyContext.h +++ b/Source/Falcor/Core/API/CopyContext.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,15 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/Resource.h" -#include "API/LowLevel/LowLevelContextData.h" +#include "Resource.h" +#include "LowLevelContextData.h" namespace Falcor { class Texture; - class Buffer; - class CopyContext + class dlldecl CopyContext { public: using SharedPtr = std::shared_ptr; @@ -78,8 +77,9 @@ namespace Falcor /** Insert a resource barrier if pViewInfo is nullptr, will transition the entire resource. Otherwise, it will only transition the subresource in the view + \return true if a barrier commands were recorded for the entire resource-view, otherwise false (for example, when the current resource state is the same as the new state or when only some subresources were transitioned) */ - virtual void resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo = nullptr); + virtual bool resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo = nullptr); /** Insert a UAV barrier */ @@ -93,7 +93,7 @@ namespace Falcor */ void copySubresource(const Texture* pDst, uint32_t dstSubresourceIdx, const Texture* pSrc, uint32_t srcSubresourceIdx); - /** Copy part of a buffer + /** Copy part of a buffer */ void copyBufferRegion(const Buffer* pDst, uint64_t dstOffset, const Buffer* pSrc, uint64_t srcOffset, uint64_t numBytes); @@ -123,7 +123,7 @@ namespace Falcor /** Read texture data Asynchronously */ ReadTextureTask::SharedPtr asyncReadTextureSubresource(const Texture* pTexture, uint32_t subresourceIndex); - + /** Get the low-level context data */ virtual const LowLevelContextData::SharedPtr& getLowLevelData() const { return mpLowLevelData; } @@ -137,9 +137,9 @@ namespace Falcor void bindDescriptorHeaps(); protected: - void textureBarrier(const Texture* pTexture, Resource::State newState); - void bufferBarrier(const Buffer* pBuffer, Resource::State newState); - void subresourceBarriers(const Texture* pTexture, Resource::State newState, const ResourceViewInfo* pViewInfo); + bool textureBarrier(const Texture* pTexture, Resource::State newState); + bool bufferBarrier(const Buffer* pBuffer, Resource::State newState); + bool subresourceBarriers(const Texture* pTexture, Resource::State newState, const ResourceViewInfo* pViewInfo); void apiSubresourceBarrier(const Texture* pTexture, Resource::State newState, Resource::State oldState, uint32_t arraySlice, uint32_t mipLevel); void updateTextureSubresources(const Texture* pTexture, uint32_t firstSubresource, uint32_t subresourceCount, const void* pData, const uvec3& offset = uvec3(0), const uvec3& size = uvec3(-1)); diff --git a/Framework/Source/API/D3D12/D3D12ApiData.h b/Source/Falcor/Core/API/D3D12/D3D12ApiData.h similarity index 94% rename from Framework/Source/API/D3D12/D3D12ApiData.h rename to Source/Falcor/Core/API/D3D12/D3D12ApiData.h index e5b0a03ef..4069913a0 100644 --- a/Framework/Source/API/D3D12/D3D12ApiData.h +++ b/Source/Falcor/Core/API/D3D12/D3D12ApiData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once +#include "Core/API/FencedPool.h" namespace Falcor { @@ -33,4 +34,4 @@ namespace Falcor { FencedPool::SharedPtr pAllocatorPool; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3D12Buffer.cpp b/Source/Falcor/Core/API/D3D12/D3D12Buffer.cpp similarity index 79% rename from Framework/Source/API/D3D12/D3D12Buffer.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Buffer.cpp index 5fc8379f9..269d4a447 100644 --- a/Framework/Source/API/D3D12/D3D12Buffer.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Buffer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,15 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Buffer.h" -#include "API/Device.h" -#include "Api/LowLevel/ResourceAllocator.h" +#include "stdafx.h" +#include "Core/API/Buffer.h" +#include "Core/API/Device.h" #include "D3D12Resource.h" namespace Falcor { - ID3D12ResourcePtr createBuffer(Buffer::State initState, size_t size, const D3D12_HEAP_PROPERTIES& heapProps, Buffer::BindFlags bindFlags) { ID3D12Device* pDevice = gpDevice->getApiHandle(); @@ -54,8 +52,9 @@ namespace Falcor D3D12_RESOURCE_STATES d3dState = getD3D12ResourceState(initState); ID3D12ResourcePtr pApiHandle; - d3d_call(pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &bufDesc, d3dState, nullptr, IID_PPV_ARGS(&pApiHandle))); - + D3D12_HEAP_FLAGS heapFlags = is_set(bindFlags, ResourceBindFlags::Shared) ? D3D12_HEAP_FLAG_SHARED : D3D12_HEAP_FLAG_NONE; + d3d_call(pDevice->CreateCommittedResource(&heapProps, heapFlags, &bufDesc, d3dState, nullptr, IID_PPV_ARGS(&pApiHandle))); + // Map and upload data if needed return pApiHandle; } @@ -80,6 +79,12 @@ namespace Falcor bool Buffer::apiInit(bool hasInitData) { + if (mCpuAccess != CpuAccess::None && is_set(mBindFlags, BindFlags::Shared)) + { + logError("Buffer::apiInit() - CpuAccess must be None for a shared resource."); + return false; + } + if (mBindFlags == BindFlags::Constant) { mSize = align_to(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, mSize); @@ -90,8 +95,9 @@ namespace Falcor mState.global = Resource::State::GenericRead; if(hasInitData == false) // Else the allocation will happen when updating the data { - mDynamicData = gpDevice->getResourceAllocator()->allocate(mSize, getBufferDataAlignment(this)); + mDynamicData = gpDevice->getUploadHeap()->allocate(mSize, getBufferDataAlignment(this)); mApiHandle = mDynamicData.pResourceHandle; + mGpuVaOffset = mDynamicData.offset; } } else if (mCpuAccess == CpuAccess::Read && mBindFlags == BindFlags::None) @@ -111,7 +117,7 @@ namespace Falcor uint64_t Buffer::getGpuAddress() const { - return mDynamicData.offset + mApiHandle->GetGPUVirtualAddress(); + return mGpuVaOffset + mApiHandle->GetGPUVirtualAddress(); } void Buffer::unmap() @@ -127,28 +133,4 @@ namespace Falcor mApiHandle->Unmap(0, &r); } } - - uint64_t Buffer::makeResident(Buffer::GpuAccessFlags flags) const - { - UNSUPPORTED_IN_D3D12("Buffer::makeResident()"); - return 0; - } - - void Buffer::evict() const - { - UNSUPPORTED_IN_D3D12("Buffer::evict()"); - } - - template - UavHandle getUavCommon(UavHandle& handle, size_t bufSize, Buffer::ApiHandle apiHandle) - { - if (handle == nullptr) - { - DescriptorHeap* pHeap = forClear ? gpDevice->getCpuUavDescriptorHeap().get() : gpDevice->getUavDescriptorHeap().get(); - handle = pHeap->allocateEntry(); - gpDevice->getApiHandle()->CreateUnorderedAccessView(apiHandle, nullptr, &desc, handle->getCpuHandle()); - } - - return handle; - } } diff --git a/Framework/Source/API/D3D12/D3D12ComputeContext.cpp b/Source/Falcor/Core/API/D3D12/D3D12ComputeContext.cpp similarity index 53% rename from Framework/Source/API/D3D12/D3D12ComputeContext.cpp rename to Source/Falcor/Core/API/D3D12/D3D12ComputeContext.cpp index 97685dd9b..264dc03ba 100644 --- a/Framework/Source/API/D3D12/D3D12ComputeContext.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12ComputeContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,36 +25,88 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/ComputeContext.h" +#include "stdafx.h" +#include "Core/API/ComputeContext.h" #include "glm/gtc/type_ptr.hpp" -#include "API/Device.h" -#include "API/DescriptorSet.h" +#include "Core/API/Device.h" namespace Falcor { - void ComputeContext::prepareForDispatch() + namespace { - assert(mpComputeState); + struct ComputeContextApiData + { + size_t refCount = 0; + CommandSignatureHandle pDispatchCommandSig = nullptr; + static void init(); + static void release(); + }; - // Apply the vars. Must be first because applyComputeVars() might cause a flush - if (mpComputeVars) + ComputeContextApiData sApiData; + + void ComputeContextApiData::init() { - applyComputeVars(); + if (!sApiData.pDispatchCommandSig) + { + D3D12_COMMAND_SIGNATURE_DESC sigDesc; + sigDesc.NumArgumentDescs = 1; + sigDesc.NodeMask = 0; + D3D12_INDIRECT_ARGUMENT_DESC argDesc; + sigDesc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS); + argDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; + sigDesc.pArgumentDescs = &argDesc; + gpDevice->getApiHandle()->CreateCommandSignature(&sigDesc, nullptr, IID_PPV_ARGS(&sApiData.pDispatchCommandSig)); + } + sApiData.refCount++; } - else + + void ComputeContextApiData::release() { - mpLowLevelData->getCommandList()->SetComputeRootSignature(RootSignature::getEmpty()->getApiHandle()); + sApiData.refCount--; + if (sApiData.refCount == 0) sApiData = {}; } - mBindComputeRootSig = false; - mpLowLevelData->getCommandList()->SetPipelineState(mpComputeState->getCSO(mpComputeVars.get())->getApiHandle()); + } + + ComputeContext::ComputeContext() + { + ComputeContextApiData::init(); + } + + ComputeContext::~ComputeContext() + { + ComputeContextApiData::release(); + } + + bool ComputeContext::prepareForDispatch(ComputeState* pState, ComputeVars* pVars) + { + assert(pState); + + // Apply the vars. Must be first because applyComputeVars() might cause a flush + if (pVars) + { + if (applyComputeVars(pVars) == false) return false; + } + else mpLowLevelData->getCommandList()->SetComputeRootSignature(RootSignature::getEmpty()->getApiHandle()); + + mpLastBoundComputeVars = pVars; + mpLowLevelData->getCommandList()->SetPipelineState(pState->getCSO(pVars)->getApiHandle()); mCommandsPending = true; + return true; } - void ComputeContext::dispatch(uint32_t groupSizeX, uint32_t groupSizeY, uint32_t groupSizeZ) + void ComputeContext::dispatch(ComputeState* pState, ComputeVars* pVars, const uvec3& dispatchSize) { - prepareForDispatch(); - mpLowLevelData->getCommandList()->Dispatch(groupSizeX, groupSizeY, groupSizeZ); + // Check dispatch dimensions. TODO: Should be moved into Falcor. + if (dispatchSize.x > D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION || + dispatchSize.y > D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION || + dispatchSize.z > D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION) + { + logError("ComputePass::execute() - Dispatch dimension exceeds maximum. Skipping."); + return; + } + + if (prepareForDispatch(pState, pVars) == false) return; + mpLowLevelData->getCommandList()->Dispatch(dispatchSize.x, dispatchSize.y, dispatchSize.z); } @@ -97,22 +149,10 @@ namespace Falcor } } - void ComputeContext::initDispatchCommandSignature() - { - D3D12_COMMAND_SIGNATURE_DESC sigDesc; - sigDesc.NumArgumentDescs = 1; - sigDesc.NodeMask = 0; - D3D12_INDIRECT_ARGUMENT_DESC argDesc; - sigDesc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS); - argDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; - sigDesc.pArgumentDescs = &argDesc; - gpDevice->getApiHandle()->CreateCommandSignature(&sigDesc, nullptr, IID_PPV_ARGS(&spDispatchCommandSig)); - } - - void ComputeContext::dispatchIndirect(const Buffer* argBuffer, uint64_t argBufferOffset) + void ComputeContext::dispatchIndirect(ComputeState* pState, ComputeVars* pVars, const Buffer* pArgBuffer, uint64_t argBufferOffset) { - prepareForDispatch(); - resourceBarrier(argBuffer, Resource::State::IndirectArg); - mpLowLevelData->getCommandList()->ExecuteIndirect(spDispatchCommandSig, 1, argBuffer->getApiHandle(), argBufferOffset, nullptr, 0); + if (prepareForDispatch(pState, pVars) == false) return; + resourceBarrier(pArgBuffer, Resource::State::IndirectArg); + mpLowLevelData->getCommandList()->ExecuteIndirect(sApiData.pDispatchCommandSig, 1, pArgBuffer->getApiHandle(), argBufferOffset, nullptr, 0); } } diff --git a/Framework/Source/API/D3D12/D3D12ComputeStateObject.cpp b/Source/Falcor/Core/API/D3D12/D3D12ComputeStateObject.cpp similarity index 95% rename from Framework/Source/API/D3D12/D3D12ComputeStateObject.cpp rename to Source/Falcor/Core/API/D3D12/D3D12ComputeStateObject.cpp index 5e36f7658..c10463715 100644 --- a/Framework/Source/API/D3D12/D3D12ComputeStateObject.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12ComputeStateObject.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,10 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" +#include "stdafx.h" +#include "Core/API/ComputeStateObject.h" #include "D3D12NvApiExDesc.h" -#include "API/ComputeStateObject.h" -#include "API/Device.h" +#include "Core/API/Device.h" namespace Falcor { @@ -40,7 +40,7 @@ namespace Falcor if (ret != NVAPI_OK) { - logError("Failed to initialize NvApi", true); + logError("Failed to initialize NvApi"); } ReflectionVar::SharedConstPtr pUav = desc.getProgramVersion()->getReflector()->getDefaultParameterBlock()->getResource("g_NvidiaExt"); @@ -69,7 +69,7 @@ namespace Falcor if (ret != NVAPI_OK || apiHandle == nullptr) { - logError("Failed to create a compute pipeline state object with NVAPI extensions", true); + logError("Failed to create a compute pipeline state object with NVAPI extensions"); return nullptr; } @@ -108,4 +108,4 @@ namespace Falcor } return true; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3D12CopyContext.cpp b/Source/Falcor/Core/API/D3D12/D3D12CopyContext.cpp similarity index 95% rename from Framework/Source/API/D3D12/D3D12CopyContext.cpp rename to Source/Falcor/Core/API/D3D12/D3D12CopyContext.cpp index 040f3e28b..070e28a4c 100644 --- a/Framework/Source/API/D3D12/D3D12CopyContext.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12CopyContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,13 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/CopyContext.h" -#include "API/Device.h" -#include "API/Buffer.h" -#include +#include "stdafx.h" +#include "Core/API/CopyContext.h" +#include "Core/API/Device.h" +#include "Core/API/Texture.h" +#include "D3D12DescriptorData.h" #include "D3D12Resource.h" -#include "LowLevel/D3D12DescriptorData.h" namespace Falcor { @@ -190,7 +189,7 @@ namespace Falcor uint8_t* pDstZ = result.data() + z * actualRowSize * mRowCount; for (uint32_t y = 0; y < mRowCount; y++) { - const uint8_t* pSrc = pSrcZ + y * footprint.Footprint.RowPitch; + const uint8_t* pSrc = pSrcZ + y * footprint.Footprint.RowPitch; uint8_t* pDst = pDstZ + y * actualRowSize; memcpy(pDst, pSrc, actualRowSize); } @@ -231,7 +230,7 @@ namespace Falcor static bool d3d12GlobalResourceBarrier(const Resource* pResource, Resource::State newState, ID3D12GraphicsCommandList* pCmdList) { - if(pResource->getGlobalState() != newState) + if (pResource->getGlobalState() != newState) { d3d12ResourceBarrier(pResource, newState, pResource->getGlobalState(), D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, pCmdList); return true; @@ -239,19 +238,21 @@ namespace Falcor return false; } - void CopyContext::textureBarrier(const Texture* pTexture, Resource::State newState) + bool CopyContext::textureBarrier(const Texture* pTexture, Resource::State newState) { bool recorded = d3d12GlobalResourceBarrier(pTexture, newState, mpLowLevelData->getCommandList()); pTexture->setGlobalState(newState); mCommandsPending = mCommandsPending || recorded; + return recorded; } - void CopyContext::bufferBarrier(const Buffer* pBuffer, Resource::State newState) + bool CopyContext::bufferBarrier(const Buffer* pBuffer, Resource::State newState) { - if (pBuffer && pBuffer->getCpuAccess() != Buffer::CpuAccess::None) return; + if (pBuffer && pBuffer->getCpuAccess() != Buffer::CpuAccess::None) return false; bool recorded = d3d12GlobalResourceBarrier(pBuffer, newState, mpLowLevelData->getCommandList()); pBuffer->setGlobalState(newState); mCommandsPending = mCommandsPending || recorded; + return recorded; } void CopyContext::apiSubresourceBarrier(const Texture* pTexture, Resource::State newState, Resource::State oldState, uint32_t arraySlice, uint32_t mipLevel) @@ -306,7 +307,7 @@ namespace Falcor { resourceBarrier(pDst, Resource::State::CopyDest); resourceBarrier(pSrc, Resource::State::CopySource); - mpLowLevelData->getCommandList()->CopyBufferRegion(pDst->getApiHandle(), dstOffset, pSrc->getApiHandle(), pSrc->getGpuAddressOffset() + srcOffset, numBytes); + mpLowLevelData->getCommandList()->CopyBufferRegion(pDst->getApiHandle(), dstOffset, pSrc->getApiHandle(), pSrc->getGpuAddressOffset() + srcOffset, numBytes); mCommandsPending = true; } @@ -330,12 +331,12 @@ namespace Falcor box.top = srcOffset.y; box.front = srcOffset.z; uint32_t mipLevel = pSrc->getSubresourceMipLevel(dstSubresource); - box.right = (size.x == -1) ? pSrc->getWidth(mipLevel) - box.left : size.x; - box.bottom = (size.y == -1) ? pSrc->getHeight(mipLevel) - box.top : size.y; - box.back = (size.z == -1) ? pSrc->getDepth(mipLevel) - box.front : size.z; + box.right = (size.x == -1) ? pSrc->getWidth(mipLevel) - box.left : size.x; + box.bottom = (size.y == -1) ? pSrc->getHeight(mipLevel) - box.top : size.y; + box.back = (size.z == -1) ? pSrc->getDepth(mipLevel) - box.front : size.z; mpLowLevelData->getCommandList()->CopyTextureRegion(&dstLoc, dstOffset.x, dstOffset.y, dstOffset.z, &srcLoc, &box); mCommandsPending = true; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorData.h b/Source/Falcor/Core/API/D3D12/D3D12DescriptorData.h similarity index 97% rename from Framework/Source/API/D3D12/LowLevel/D3D12DescriptorData.h rename to Source/Falcor/Core/API/D3D12/D3D12DescriptorData.h index 2b4897377..f7c237b84 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorData.h +++ b/Source/Falcor/Core/API/D3D12/D3D12DescriptorData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorHeap.cpp b/Source/Falcor/Core/API/D3D12/D3D12DescriptorHeap.cpp similarity index 83% rename from Framework/Source/API/D3D12/LowLevel/D3D12DescriptorHeap.cpp rename to Source/Falcor/Core/API/D3D12/D3D12DescriptorHeap.cpp index be1a3f6a7..ab846ded1 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorHeap.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12DescriptorHeap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,13 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "D3D12DescriptorHeap.h" -#include "API/Device.h" +#include "Core/API/Device.h" namespace Falcor { - D3D12DescriptorHeap::D3D12DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t chunkCount) : mChunkCount(chunkCount), mType (type) + D3D12DescriptorHeap::D3D12DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t chunkCount) : mMaxChunkCount(chunkCount), mType(type) { DeviceHandle pDevice = gpDevice->getApiHandle(); mDescriptorSize = pDevice->GetDescriptorHandleIncrementSize(type); @@ -100,7 +100,7 @@ namespace Falcor if (mpCurrentChunk) { // Check if the current chunk has enough space - if (mpCurrentChunk->chunkCount * kDescPerChunk - mpCurrentChunk->currentDesc >= descCount) return true; + if (mpCurrentChunk->getRemainingDescs() >= descCount) return true; if (mpCurrentChunk->allocCount == 0) { @@ -116,18 +116,26 @@ namespace Falcor // Need a new chunk uint32_t chunkCount = (descCount + kDescPerChunk - 1) / kDescPerChunk; - // Take chunk from top of sorted free list if chunk is large enough. - // TODO: Sort by offset to find larger contiguous chunks if it fails. - if (!mFreeChunks.empty() && (chunkCount <= mFreeChunks.top()->chunkCount)) + if (chunkCount == 1 && mFreeChunks.empty() == false) { - mpCurrentChunk = mFreeChunks.top(); - mFreeChunks.pop(); - mpCurrentChunk->reset(); + mpCurrentChunk = mFreeChunks.back(); + mFreeChunks.pop_back(); return true; } + else if (chunkCount > 1 && mFreeLargeChunks.empty() == false) + { + // Find the smallest chunk big enough for the allocation + auto it = std::lower_bound(mFreeLargeChunks.begin(), mFreeLargeChunks.end(), chunkCount, ChunkComparator()); + if (it != mFreeLargeChunks.end()) + { + mpCurrentChunk = *it; + mFreeLargeChunks.erase(it); + return true; + } + } // No free chunks. Allocate - if (mAllocatedChunks + chunkCount > mChunkCount) + if (mAllocatedChunks + chunkCount > mMaxChunkCount) { return false; } @@ -140,9 +148,11 @@ namespace Falcor void D3D12DescriptorHeap::releaseChunk(Chunk::SharedPtr pChunk) { pChunk->allocCount--; - if(pChunk->allocCount == 0 && (pChunk != mpCurrentChunk)) + if (pChunk->allocCount == 0 && (pChunk != mpCurrentChunk)) { - mFreeChunks.push(pChunk); + pChunk->reset(); + if(pChunk->chunkCount == 1) mFreeChunks.push_back(pChunk); + else mFreeLargeChunks.insert(pChunk); } } diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorHeap.h b/Source/Falcor/Core/API/D3D12/D3D12DescriptorHeap.h similarity index 86% rename from Framework/Source/API/D3D12/LowLevel/D3D12DescriptorHeap.h rename to Source/Falcor/Core/API/D3D12/D3D12DescriptorHeap.h index 25de93a4c..762d99816 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorHeap.h +++ b/Source/Falcor/Core/API/D3D12/D3D12DescriptorHeap.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -73,7 +73,7 @@ namespace Falcor const ApiHandle& getApiHandle() const { return mApiHandle; } D3D12_DESCRIPTOR_HEAP_TYPE getType() const { return mType; } - uint32_t getReservedChunkCount() const { return mChunkCount; } + uint32_t getReservedChunkCount() const { return mMaxChunkCount; } uint32_t getDescriptorSize() const { return mDescriptorSize; } private: friend Allocation; @@ -86,7 +86,7 @@ namespace Falcor CpuHandle mCpuHeapStart = {}; GpuHandle mGpuHeapStart = {}; uint32_t mDescriptorSize; - uint32_t mChunkCount = 0; + const uint32_t mMaxChunkCount = 0; uint32_t mAllocatedChunks = 0; ApiHandle mApiHandle; D3D12_DESCRIPTOR_HEAP_TYPE mType; @@ -99,6 +99,7 @@ namespace Falcor void reset() { allocCount = 0; currentDesc = 0; } uint32_t getCurrentAbsoluteIndex() const { return chunkIndex * kDescPerChunk + currentDesc; } + uint32_t getRemainingDescs() const { return chunkCount * kDescPerChunk - currentDesc; } uint32_t chunkIndex = 0; uint32_t chunkCount = 1; // For outstanding requests we can allocate more then a single chunk. This is the number of chunks we actually allocated @@ -109,15 +110,22 @@ namespace Falcor // Helper to compare Chunk::SharedPtr types struct ChunkComparator { - bool operator()(Chunk::SharedPtr lhs, Chunk::SharedPtr rhs) + bool operator()(const Chunk::SharedPtr& lhs, const Chunk::SharedPtr& rhs) { return lhs->chunkCount < rhs->chunkCount; } + + bool operator()(const Chunk::SharedPtr& lhs, uint32_t rhs) + { + return lhs->chunkCount < rhs; + }; }; - Chunk::SharedPtr mpCurrentChunk; bool setupCurrentChunk(uint32_t descCount); void releaseChunk(Chunk::SharedPtr pChunk); - std::priority_queue, ChunkComparator> mFreeChunks; // Priority queue that keeps free list sorted by chunk count (largest first) + + Chunk::SharedPtr mpCurrentChunk; + std::vector mFreeChunks; // Free list for standard sized chunks (1 chunk * kDescPerChunk) + std::multiset mFreeLargeChunks; // Free list for large chunks with the capacity of multiple chunks (>1 chunk * kDescPerChunk) }; } diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorPool.cpp b/Source/Falcor/Core/API/D3D12/D3D12DescriptorPool.cpp similarity index 88% rename from Framework/Source/API/D3D12/LowLevel/D3D12DescriptorPool.cpp rename to Source/Falcor/Core/API/D3D12/D3D12DescriptorPool.cpp index bc1018d62..b98bed392 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorPool.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12DescriptorPool.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/DescriptorPool.h" -#include "D3D12DescriptorHeap.h" +#include "stdafx.h" +#include "Core/API/DescriptorPool.h" #include "D3D12DescriptorData.h" namespace Falcor @@ -38,10 +37,12 @@ namespace Falcor { case DescriptorPool::Type::TextureSrv: case DescriptorPool::Type::TextureUav: - case DescriptorPool::Type::StructuredBufferSrv: - case DescriptorPool::Type::StructuredBufferUav: + case DescriptorPool::Type::RawBufferSrv: + case DescriptorPool::Type::RawBufferUav: case DescriptorPool::Type::TypedBufferSrv: case DescriptorPool::Type::TypedBufferUav: + case DescriptorPool::Type::StructuredBufferSrv: + case DescriptorPool::Type::StructuredBufferUav: case DescriptorPool::Type::Cbv: return D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; case DescriptorPool::Type::Dsv: @@ -59,15 +60,15 @@ namespace Falcor bool DescriptorPool::apiInit() { // Find out how many heaps we need - static_assert(DescriptorPool::kTypeCount == 10, "Unexpected desc count, make sure all desc types are supported"); + static_assert(DescriptorPool::kTypeCount == 12, "Unexpected desc count, make sure all desc types are supported"); uint32_t descCount[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES] = { 0 }; descCount[D3D12_DESCRIPTOR_HEAP_TYPE_RTV] = mDesc.mDescCount[(uint32_t)Type::Rtv]; descCount[D3D12_DESCRIPTOR_HEAP_TYPE_DSV] = mDesc.mDescCount[(uint32_t)Type::Dsv]; descCount[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER] = mDesc.mDescCount[(uint32_t)Type::Sampler]; descCount[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] = mDesc.mDescCount[(uint32_t)Type::Cbv]; - descCount[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] += mDesc.mDescCount[(uint32_t)Type::TextureUav] + mDesc.mDescCount[(uint32_t)Type::TypedBufferUav] + mDesc.mDescCount[(uint32_t)Type::StructuredBufferUav]; - descCount[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] += mDesc.mDescCount[(uint32_t)Type::TextureSrv] + mDesc.mDescCount[(uint32_t)Type::TypedBufferSrv] + mDesc.mDescCount[(uint32_t)Type::StructuredBufferSrv]; + descCount[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] += mDesc.mDescCount[(uint32_t)Type::TextureSrv] + mDesc.mDescCount[(uint32_t)Type::RawBufferSrv] + mDesc.mDescCount[(uint32_t)Type::TypedBufferSrv] + mDesc.mDescCount[(uint32_t)Type::StructuredBufferSrv]; + descCount[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] += mDesc.mDescCount[(uint32_t)Type::TextureUav] + mDesc.mDescCount[(uint32_t)Type::RawBufferUav] + mDesc.mDescCount[(uint32_t)Type::TypedBufferUav] + mDesc.mDescCount[(uint32_t)Type::StructuredBufferUav]; mpApiData = std::make_shared(); for (uint32_t i = 0; i < arraysize(mpApiData->pHeaps); i++) @@ -86,4 +87,4 @@ namespace Falcor assert(heapIndex < arraysize(mpApiData->pHeaps)); return mpApiData->pHeaps[heapIndex]->getApiHandle(); } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorSet.cpp b/Source/Falcor/Core/API/D3D12/D3D12DescriptorSet.cpp similarity index 95% rename from Framework/Source/API/D3D12/LowLevel/D3D12DescriptorSet.cpp rename to Source/Falcor/Core/API/D3D12/D3D12DescriptorSet.cpp index 6f71d2cdb..d07178ddd 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12DescriptorSet.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12DescriptorSet.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/DescriptorSet.h" -#include "D3D12DescriptorHeap.h" +#include "stdafx.h" +#include "Core/API/DescriptorSet.h" #include "D3D12DescriptorData.h" -#include "API/Device.h" +#include "Core/API/Device.h" +#include "Core/API/CopyContext.h" namespace Falcor { @@ -117,8 +117,8 @@ namespace Falcor pCtx->getLowLevelData()->getCommandList()->SetComputeRootDescriptorTable(rootIndex, getGpuHandle(0)); } - void DescriptorSet::setCbv(uint32_t rangeIndex, uint32_t descIndex, const ConstantBufferView::SharedPtr& pView) + void DescriptorSet::setCbv(uint32_t rangeIndex, uint32_t descIndex, ConstantBufferView* pView) { setCpuHandle(this, rangeIndex, descIndex, pView->getApiHandle()->getCpuHandle(0)); } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3D12Device.cpp b/Source/Falcor/Core/API/D3D12/D3D12Device.cpp similarity index 86% rename from Framework/Source/API/D3D12/D3D12Device.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Device.cpp index 4b5ad2583..8f6067836 100644 --- a/Framework/Source/API/D3D12/D3D12Device.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Device.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Sample.h" -#include "API/Device.h" -#include "API/LowLevel/GpuFence.h" +#include "stdafx.h" +#include "Core/API/Device.h" namespace Falcor { @@ -95,12 +93,12 @@ namespace Falcor return (D3D_FEATURE_LEVEL)0; } - IDXGISwapChain3Ptr createDxgiSwapChain(IDXGIFactory4* pFactory, const Window* pWindow, ID3D12CommandQueue* pCommandQueue, ResourceFormat colorFormat) + IDXGISwapChain3Ptr createDxgiSwapChain(IDXGIFactory4* pFactory, const Window* pWindow, ID3D12CommandQueue* pCommandQueue, ResourceFormat colorFormat, uint32_t bufferCount) { DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; - swapChainDesc.BufferCount = kDefaultSwapChainBuffers; - swapChainDesc.Width = pWindow->getClientAreaWidth(); - swapChainDesc.Height = pWindow->getClientAreaHeight(); + swapChainDesc.BufferCount = bufferCount; + swapChainDesc.Width = pWindow->getClientAreaSize().x; + swapChainDesc.Height = pWindow->getClientAreaSize().y; // Flip mode doesn't support SRGB formats, so we strip them down when creating the resource. We will create the RTV as SRGB instead. // More details at the end of https://msdn.microsoft.com/en-us/library/windows/desktop/bb173064.aspx swapChainDesc.Format = getDxgiFormat(srgbToLinearFormat(colorFormat)); @@ -145,6 +143,12 @@ namespace Falcor DeviceHandle pDevice; D3D_FEATURE_LEVEL deviceFeatureLevel; + // Read FALCOR_GPU_DEVICE_ID environment variable or select first GPU device + const int selectedGpuDeviceId = ([] () { + std::string str; + return getEnvironmentVariable("FALCOR_GPU_DEVICE_ID", str) ? std::stoi(str) : 0; + })(); + auto createMaxFeatureLevel = [&](const D3D_FEATURE_LEVEL* pFeatureLevels, uint32_t featureLevelCount) -> bool { for (uint32_t i = 0; i < featureLevelCount; i++) @@ -159,6 +163,7 @@ namespace Falcor return false; }; + int gpuDeviceId = 0; for (uint32_t i = 0; DXGI_ERROR_NOT_FOUND != pFactory->EnumAdapters1(i, &pAdapter); i++) { DXGI_ADAPTER_DESC1 desc; @@ -167,6 +172,9 @@ namespace Falcor // Skip SW adapters if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) continue; + // Skip to selected device id + if (gpuDeviceId++ < selectedGpuDeviceId) continue; + if (requestedFeatureLevel == 0) createMaxFeatureLevel(kFeatureLevels, arraysize(kFeatureLevels)); else createMaxFeatureLevel(&requestedFeatureLevel, 1); @@ -232,9 +240,9 @@ namespace Falcor } } - bool Device::getApiFboData(uint32_t width, uint32_t height, ResourceFormat colorFormat, ResourceFormat depthFormat, std::vector& apiHandles, uint32_t& currentBackBufferIndex) + bool Device::getApiFboData(uint32_t width, uint32_t height, ResourceFormat colorFormat, ResourceFormat depthFormat, ResourceHandle apiHandles[kSwapChainBuffersCount], uint32_t& currentBackBufferIndex) { - for (uint32_t i = 0; i < mSwapChainBufferCount; i++) + for (uint32_t i = 0; i < kSwapChainBuffersCount; i++) { HRESULT hr = mpApiData->pSwapChain->GetBuffer(i, IID_PPV_ARGS(&apiHandles[i])); if (FAILED(hr)) @@ -260,16 +268,16 @@ namespace Falcor void Device::apiPresent() { - mpApiData->pSwapChain->Present(mVsyncOn ? 1 : 0, 0); - mCurrentBackBufferIndex = (mCurrentBackBufferIndex + 1) % mSwapChainBufferCount; + mpApiData->pSwapChain->Present(mDesc.enableVsync ? 1 : 0, 0); + mCurrentBackBufferIndex = (mCurrentBackBufferIndex + 1) % kSwapChainBuffersCount; } - bool Device::apiInit(const Desc& desc) + bool Device::apiInit() { DeviceApiData* pData = new DeviceApiData; mpApiData = pData; UINT dxgiFlags = 0; - if (desc.enableDebugLayer) + if (mDesc.enableDebugLayer) { ID3D12DebugPtr pDx12Debug; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pDx12Debug)))) @@ -282,15 +290,12 @@ namespace Falcor // Create the DXGI factory d3d_call(CreateDXGIFactory2(dxgiFlags, IID_PPV_ARGS(&mpApiData->pDxgiFactory))); - mApiHandle = createDevice(mpApiData->pDxgiFactory, getD3DFeatureLevel(desc.apiMajorVersion, desc.apiMinorVersion), desc.experimentalFeatures); - if (mApiHandle == nullptr) - { - return false; - } + mApiHandle = createDevice(mpApiData->pDxgiFactory, getD3DFeatureLevel(mDesc.apiMajorVersion, mDesc.apiMinorVersion), mDesc.experimentalFeatures); + if (mApiHandle == nullptr) return false; mSupportedFeatures = getSupportedFeatures(mApiHandle); - if (desc.enableDebugLayer) + if (mDesc.enableDebugLayer) { MAKE_SMART_COM_PTR(ID3D12InfoQueue); ID3D12InfoQueuePtr pInfoQueue; @@ -299,16 +304,20 @@ namespace Falcor { D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, + D3D12_MESSAGE_ID_COPY_DESCRIPTORS_INVALID_RANGES }; D3D12_INFO_QUEUE_FILTER f = {}; f.DenyList.NumIDs = arraysize(hideMessages); f.DenyList.pIDList = hideMessages; pInfoQueue->AddStorageFilterEntries(&f); + + // Break on DEVICE_REMOVAL_PROCESS_AT_FAULT + pInfoQueue->SetBreakOnID(D3D12_MESSAGE_ID_DEVICE_REMOVAL_PROCESS_AT_FAULT, true); } for (uint32_t i = 0; i < kQueueTypeCount; i++) { - for (uint32_t j = 0; j < desc.cmdQueues[i]; j++) + for (uint32_t j = 0; j < mDesc.cmdQueues[i]; j++) { // Create the command queue D3D12_COMMAND_QUEUE_DESC cqDesc = {}; @@ -329,20 +338,13 @@ namespace Falcor uint64_t freq; d3d_call(getCommandQueueHandle(LowLevelContextData::CommandQueueType::Direct, 0)->GetTimestampFrequency(&freq)); mGpuTimestampFrequency = 1000.0 / (double)freq; - - mpRenderContext = RenderContext::create(mCmdQueues[(uint32_t)LowLevelContextData::CommandQueueType::Direct][0]); - return createSwapChain(desc.colorFormat); + return createSwapChain(mDesc.colorFormat); } bool Device::createSwapChain(ResourceFormat colorFormat) { - mpApiData->pSwapChain = createDxgiSwapChain(mpApiData->pDxgiFactory, mpWindow.get(), mpRenderContext->getLowLevelData()->getCommandQueue(), colorFormat); - if (mpApiData->pSwapChain == nullptr) - { - return false; - } - - mpSwapChainFbos.resize(mSwapChainBufferCount); + mpApiData->pSwapChain = createDxgiSwapChain(mpApiData->pDxgiFactory, mpWindow.get(), getCommandQueueHandle(LowLevelContextData::CommandQueueType::Direct, 0), colorFormat, kSwapChainBuffersCount); + if (mpApiData->pSwapChain == nullptr) return false; return true; } @@ -350,7 +352,7 @@ namespace Falcor { DXGI_SWAP_CHAIN_DESC desc; d3d_call(mpApiData->pSwapChain->GetDesc(&desc)); - d3d_call(mpApiData->pSwapChain->ResizeBuffers(mSwapChainBufferCount, width, height, desc.BufferDesc.Format, desc.Flags)); + d3d_call(mpApiData->pSwapChain->ResizeBuffers(kSwapChainBuffersCount, width, height, desc.BufferDesc.Format, desc.Flags)); } bool Device::isWindowOccluded() const @@ -361,9 +363,4 @@ namespace Falcor } return mpApiData->isWindowOccluded; } - - bool Device::isExtensionSupported(const std::string& name) const - { - return _ENABLE_NVAPI; - } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3D12Fbo.cpp b/Source/Falcor/Core/API/D3D12/D3D12Fbo.cpp similarity index 83% rename from Framework/Source/API/D3D12/D3D12Fbo.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Fbo.cpp index 5964e28fe..23bfc9612 100644 --- a/Framework/Source/API/D3D12/D3D12Fbo.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Fbo.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,29 +25,29 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/FBO.h" -#include "API/D3D12/D3DViews.h" -#include "API/Device.h" -#include "API/ResourceViews.h" +#include "stdafx.h" +#include "Core/API/FBO.h" namespace Falcor { + template + ViewType getViewDimension(Resource::Type type, bool isArray); + template<> - D3D12_RTV_DIMENSION getViewDimension(Texture::Type type, bool isTextureArray) + D3D12_RTV_DIMENSION getViewDimension(Resource::Type type, bool isTextureArray) { switch(type) { - case Texture::Type::Texture1D: + case Resource::Type::Texture1D: return (isTextureArray) ? D3D12_RTV_DIMENSION_TEXTURE1DARRAY : D3D12_RTV_DIMENSION_TEXTURE1D; - case Texture::Type::Texture2D: + case Resource::Type::Texture2D: return (isTextureArray) ? D3D12_RTV_DIMENSION_TEXTURE2DARRAY : D3D12_RTV_DIMENSION_TEXTURE2D; - case Texture::Type::Texture3D: + case Resource::Type::Texture3D: assert(isTextureArray == false); return D3D12_RTV_DIMENSION_TEXTURE3D; - case Texture::Type::Texture2DMultisample: + case Resource::Type::Texture2DMultisample: return (isTextureArray) ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY : D3D12_RTV_DIMENSION_TEXTURE2DMS; - case Texture::Type::TextureCube: + case Resource::Type::TextureCube: return D3D12_RTV_DIMENSION_TEXTURE2DARRAY; default: should_not_get_here(); @@ -56,17 +56,17 @@ namespace Falcor } template<> - D3D12_DSV_DIMENSION getViewDimension(Texture::Type type, bool isTextureArray) + D3D12_DSV_DIMENSION getViewDimension(Resource::Type type, bool isTextureArray) { switch(type) { - case Texture::Type::Texture1D: + case Resource::Type::Texture1D: return (isTextureArray) ? D3D12_DSV_DIMENSION_TEXTURE1DARRAY : D3D12_DSV_DIMENSION_TEXTURE1D; - case Texture::Type::Texture2D: + case Resource::Type::Texture2D: return (isTextureArray) ? D3D12_DSV_DIMENSION_TEXTURE2DARRAY : D3D12_DSV_DIMENSION_TEXTURE2D; - case Texture::Type::Texture2DMultisample: + case Resource::Type::Texture2DMultisample: return (isTextureArray) ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY : D3D12_DSV_DIMENSION_TEXTURE2DMS; - case Texture::Type::TextureCube: + case Resource::Type::TextureCube: return D3D12_DSV_DIMENSION_TEXTURE2DARRAY; default: should_not_get_here(); diff --git a/Framework/Source/API/D3D12/D3DFormats.cpp b/Source/Falcor/Core/API/D3D12/D3D12Formats.cpp similarity index 98% rename from Framework/Source/API/D3D12/D3DFormats.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Formats.cpp index 8002a58b5..3f0bc2762 100644 --- a/Framework/Source/API/D3D12/D3DFormats.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Formats.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,8 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Device.h" +#include "stdafx.h" +#include "Core/API/Device.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12GpuFence.cpp b/Source/Falcor/Core/API/D3D12/D3D12GpuFence.cpp similarity index 87% rename from Framework/Source/API/D3D12/LowLevel/D3D12GpuFence.cpp rename to Source/Falcor/Core/API/D3D12/D3D12GpuFence.cpp index bf3b38cdb..d4793868f 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12GpuFence.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12GpuFence.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/GpuFence.h" -#include "API/Device.h" +#include "stdafx.h" +#include "Core/API/GpuFence.h" +#include "Core/API/Device.h" namespace Falcor { @@ -72,12 +72,15 @@ namespace Falcor d3d_call(pQueue->Wait(mApiHandle, mCpuValue - 1)); } - void GpuFence::syncCpu() + void GpuFence::syncCpu(std::optional val) { + uint64_t syncVal = val ? val.value() : mCpuValue - 1; + assert(syncVal <= mCpuValue - 1); + uint64_t gpuVal = getGpuValue(); - if (gpuVal < mCpuValue - 1) + if (gpuVal < syncVal) { - d3d_call(mApiHandle->SetEventOnCompletion(mCpuValue - 1, mpApiData->eventHandle)); + d3d_call(mApiHandle->SetEventOnCompletion(syncVal, mpApiData->eventHandle)); WaitForSingleObject(mpApiData->eventHandle, INFINITE); } } diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12ResourceAllocator.cpp b/Source/Falcor/Core/API/D3D12/D3D12GpuMemoryHeap.cpp similarity index 58% rename from Framework/Source/API/D3D12/LowLevel/D3D12ResourceAllocator.cpp rename to Source/Falcor/Core/API/D3D12/D3D12GpuMemoryHeap.cpp index 037119edd..90855011f 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12ResourceAllocator.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12GpuMemoryHeap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,52 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/ResourceAllocator.h" -#include "API/Buffer.h" -#include "API/D3D12/D3D12Resource.h" +#include "stdafx.h" +#include "Core/API/GpuMemoryHeap.h" +#include "D3D12Resource.h" namespace Falcor { ID3D12ResourcePtr createBuffer(Buffer::State initState, size_t size, const D3D12_HEAP_PROPERTIES& heapProps, Buffer::BindFlags bindFlags); - - void ResourceAllocator::initBasePageData(BaseData& data, size_t size) + + namespace + { + D3D12_HEAP_PROPERTIES getHeapProps(GpuMemoryHeap::Type t) + { + switch (t) + { + case GpuMemoryHeap::Type::Default: + return kDefaultHeapProps; + case GpuMemoryHeap::Type::Upload: + return kUploadHeapProps; + case GpuMemoryHeap::Type::Readback: + return kReadbackHeapProps; + default: + should_not_get_here(); + return D3D12_HEAP_PROPERTIES(); + } + } + + Buffer::State getInitState(GpuMemoryHeap::Type t) + { + switch (t) + { + case GpuMemoryHeap::Type::Default: + return Buffer::State::Common; + case GpuMemoryHeap::Type::Upload: + return Buffer::State::GenericRead; + case GpuMemoryHeap::Type::Readback: + return Buffer::State::CopyDest; + default: + should_not_get_here(); + return Buffer::State::Undefined; + } + } + } + + void GpuMemoryHeap::initBasePageData(BaseData& data, size_t size) { - data.pResourceHandle = createBuffer(Buffer::State::GenericRead, size, kUploadHeapProps, Buffer::BindFlags::None); + data.pResourceHandle = createBuffer(getInitState(mType), size, getHeapProps(mType), Buffer::BindFlags::None); data.offset = 0; D3D12_RANGE readRange = {}; d3d_call(data.pResourceHandle->Map(0, &readRange, (void**)&data.pData)); diff --git a/Framework/Source/API/D3D12/D3D12GpuTimer.cpp b/Source/Falcor/Core/API/D3D12/D3D12GpuTimer.cpp similarity index 93% rename from Framework/Source/API/D3D12/D3D12GpuTimer.cpp rename to Source/Falcor/Core/API/D3D12/D3D12GpuTimer.cpp index f76d18837..e741048ab 100644 --- a/Framework/Source/API/D3D12/D3D12GpuTimer.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12GpuTimer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/GpuTimer.h" -#include "API/Buffer.h" -#include "API/Device.h" +#include "stdafx.h" +#include "Core/API/GpuTimer.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3D12GraphicsStateObject.cpp b/Source/Falcor/Core/API/D3D12/D3D12GraphicsStateObject.cpp similarity index 94% rename from Framework/Source/API/D3D12/D3D12GraphicsStateObject.cpp rename to Source/Falcor/Core/API/D3D12/D3D12GraphicsStateObject.cpp index 0c7e90c93..210341fba 100644 --- a/Framework/Source/API/D3D12/D3D12GraphicsStateObject.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12GraphicsStateObject.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,13 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" +#include "stdafx.h" +#include "Core/API/GraphicsStateObject.h" #include "D3D12NvApiExDesc.h" -#include "API/GraphicsStateObject.h" -#include "API/D3D12/D3D12State.h" -#include "API/FBO.h" -#include "API/Texture.h" -#include "API/Device.h" +#include "Core/API/Device.h" +#include "D3D12State.h" namespace Falcor { @@ -43,7 +41,7 @@ namespace Falcor if (ret != NVAPI_OK) { - logError("Failed to initialize NvApi", true); + logError("Failed to initialize NvApi"); } if (desc.getSinglePassStereoEnabled()) @@ -81,7 +79,7 @@ namespace Falcor if (ret != NVAPI_OK || apiHandle == nullptr) { - logError("Failed to create a graphics pipeline state object with NVAPI extensions", true); + logError("Failed to create a graphics pipeline state object with NVAPI extensions"); return nullptr; } @@ -117,4 +115,4 @@ namespace Falcor } return true; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12LowLevelContextData.cpp b/Source/Falcor/Core/API/D3D12/D3D12LowLevelContextData.cpp similarity index 95% rename from Framework/Source/API/D3D12/LowLevel/D3D12LowLevelContextData.cpp rename to Source/Falcor/Core/API/D3D12/D3D12LowLevelContextData.cpp index fe49838c9..ac7f6a827 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12LowLevelContextData.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12LowLevelContextData.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,10 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/LowLevelContextData.h" -#include "API/Device.h" -#include "API/D3D12/D3D12ApiData.h" +#include "stdafx.h" +#include "Core/API/LowLevelContextData.h" +#include "Core/API/Device.h" +#include "D3D12ApiData.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3D12NvApiExDesc.h b/Source/Falcor/Core/API/D3D12/D3D12NvApiExDesc.h similarity index 98% rename from Framework/Source/API/D3D12/D3D12NvApiExDesc.h rename to Source/Falcor/Core/API/D3D12/D3D12NvApiExDesc.h index 960ebc3e1..179ea1b2a 100644 --- a/Framework/Source/API/D3D12/D3D12NvApiExDesc.h +++ b/Source/Falcor/Core/API/D3D12/D3D12NvApiExDesc.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3D12QueryHeap.cpp b/Source/Falcor/Core/API/D3D12/D3D12QueryHeap.cpp similarity index 94% rename from Framework/Source/API/D3D12/D3D12QueryHeap.cpp rename to Source/Falcor/Core/API/D3D12/D3D12QueryHeap.cpp index a7cd96f3e..dafc06ee4 100644 --- a/Framework/Source/API/D3D12/D3D12QueryHeap.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12QueryHeap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Api/QueryHeap.h" -#include "Api/Device.h" +#include "stdafx.h" +#include "Core/API/QueryHeap.h" +#include "Core/API/Device.h" namespace Falcor { @@ -57,4 +57,4 @@ namespace Falcor d3d_call(pDevice->CreateQueryHeap(&desc, IID_PPV_ARGS(&mApiHandle))); } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3D12RasterizerState.cpp b/Source/Falcor/Core/API/D3D12/D3D12RasterizerState.cpp similarity index 93% rename from Framework/Source/API/D3D12/D3D12RasterizerState.cpp rename to Source/Falcor/Core/API/D3D12/D3D12RasterizerState.cpp index 9688baa8a..50743ee9c 100644 --- a/Framework/Source/API/D3D12/D3D12RasterizerState.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12RasterizerState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,8 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/RasterizerState.h" +#include "stdafx.h" +#include "Core/API/RasterizerState.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3D12RenderContext.cpp b/Source/Falcor/Core/API/D3D12/D3D12RenderContext.cpp similarity index 62% rename from Framework/Source/API/D3D12/D3D12RenderContext.cpp rename to Source/Falcor/Core/API/D3D12/D3D12RenderContext.cpp index 5f1caeb67..33fc17561 100644 --- a/Framework/Source/API/D3D12/D3D12RenderContext.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12RenderContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,59 +25,114 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/RenderContext.h" -#include "API/Device.h" +#include "stdafx.h" +#include "Core/API/RenderContext.h" +#include "Core/API/Device.h" #include "glm/gtc/type_ptr.hpp" -#include "D3D12Resource.h" -#include "API/D3D12/D3D12State.h" -#include "API/DescriptorSet.h" -#include "Experimental/Raytracing/RtProgramVars.h" -#include "Experimental/Raytracing/RtState.h" +#include "D3D12State.h" +#include "Raytracing/RtProgramVars.h" +#include "Raytracing/RtState.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" namespace Falcor { - static void initBlitData() + namespace { - if (gBlitData.pVars == nullptr) + struct RenderContextApiData { - gBlitData.pPass = FullScreenPass::create("Framework/Shaders/Blit.vs.slang", "Framework/Shaders/Blit.ps.slang"); - gBlitData.pVars = GraphicsVars::create(gBlitData.pPass->getProgram()->getReflector()); - gBlitData.pState = GraphicsState::create(); - - gBlitData.pSrcRectBuffer = gBlitData.pVars->getConstantBuffer("SrcRectCB"); - gBlitData.offsetVarOffset = (uint32_t)gBlitData.pSrcRectBuffer->getVariableOffset("gOffset"); - gBlitData.scaleVarOffset = (uint32_t)gBlitData.pSrcRectBuffer->getVariableOffset("gScale"); - gBlitData.prevSrcRectOffset = vec2(-1.0f); - gBlitData.prevSrcReftScale = vec2(-1.0f); - - Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - gBlitData.pLinearSampler = Sampler::create(desc); - desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - gBlitData.pPointSampler = Sampler::create(desc); - const auto& pDefaultBlockReflection = gBlitData.pPass->getProgram()->getReflector()->getDefaultParameterBlock(); - gBlitData.texBindLoc = pDefaultBlockReflection->getResourceBinding("gTex"); - gBlitData.samplerBindLoc = pDefaultBlockReflection->getResourceBinding("gSampler"); + size_t refCount = 0; + + CommandSignatureHandle pDrawCommandSig; + CommandSignatureHandle pDrawIndexCommandSig; + + struct + { + std::shared_ptr pPass; + Fbo::SharedPtr pFbo; + + Sampler::SharedPtr pLinearSampler; + Sampler::SharedPtr pPointSampler; + + ConstantBuffer::SharedPtr pSrcRectBuffer; + vec2 prevSrcRectOffset = vec2(0, 0); + vec2 prevSrcReftScale = vec2(0, 0); + + // Variable offsets in constant buffer + size_t offsetVarOffset = ConstantBuffer::kInvalidOffset; + size_t scaleVarOffset = ConstantBuffer::kInvalidOffset; + ProgramReflection::BindLocation texBindLoc; + } blitData; + + static void init(); + static void release(); + }; + + RenderContextApiData sApiData; + + void RenderContextApiData::init() + { + auto& blitData = sApiData.blitData; + if (blitData.pPass == nullptr) + { + // Init the blit data + Program::Desc d; + d.addShaderLibrary("Framework/Shaders/Blit.slang").vsEntry("vs").psEntry("ps"); + blitData.pPass = FullScreenPass::create(d); + blitData.pFbo = Fbo::create(); + + blitData.pSrcRectBuffer = blitData.pPass->getVars()->getConstantBuffer("SrcRectCB"); + blitData.offsetVarOffset = (uint32_t)blitData.pSrcRectBuffer->getVariableOffset("gOffset"); + blitData.scaleVarOffset = (uint32_t)blitData.pSrcRectBuffer->getVariableOffset("gScale"); + blitData.prevSrcRectOffset = vec2(-1.0f); + blitData.prevSrcReftScale = vec2(-1.0f); + + Sampler::Desc desc; + desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + blitData.pLinearSampler = Sampler::create(desc); + desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + blitData.pPointSampler = Sampler::create(desc); + const auto& pDefaultBlockReflection = blitData.pPass->getProgram()->getReflector()->getDefaultParameterBlock(); + blitData.texBindLoc = pDefaultBlockReflection->getResourceBinding("gTex"); + + // Init the draw signature + D3D12_COMMAND_SIGNATURE_DESC sigDesc; + sigDesc.NumArgumentDescs = 1; + sigDesc.NodeMask = 0; + D3D12_INDIRECT_ARGUMENT_DESC argDesc; + + //Draw + sigDesc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS); + argDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW; + sigDesc.pArgumentDescs = &argDesc; + gpDevice->getApiHandle()->CreateCommandSignature(&sigDesc, nullptr, IID_PPV_ARGS(&sApiData.pDrawCommandSig)); + + //Draw index + sigDesc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS); + argDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; + sigDesc.pArgumentDescs = &argDesc; + gpDevice->getApiHandle()->CreateCommandSignature(&sigDesc, nullptr, IID_PPV_ARGS(&sApiData.pDrawIndexCommandSig)); + } + + sApiData.refCount++; + } + + void RenderContextApiData::release() + { + sApiData.refCount--; + if (sApiData.refCount == 0) sApiData = {}; } } - void releaseApiData() + RenderContext::RenderContext() { - gBlitData.pSrcRectBuffer = nullptr; - gBlitData.pVars = nullptr; - gBlitData.pPass = nullptr; - gBlitData.pState = nullptr; - gpDrawCommandSig = nullptr; - gpDrawIndexCommandSig = nullptr; + RenderContextApiData::init(); } RenderContext::~RenderContext() { - releaseApiData(); + RenderContextApiData::release(); } - RenderContext::SharedPtr RenderContext::create(CommandQueueHandle queue) { SharedPtr pCtx = SharedPtr(new RenderContext()); @@ -152,7 +207,7 @@ namespace Falcor { for (uint32_t i = 0; i < colorTargets; i++) { - auto& pTexture = pFbo->getColorTexture(i); + auto pTexture = pFbo->getColorTexture(i); if (pTexture) { pRTV[i] = pFbo->getRenderTargetView(i)->getApiHandle()->getCpuHandle(0); @@ -233,118 +288,93 @@ namespace Falcor pList->RSSetScissorRects(D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE, (D3D12_RECT*)sc); } - void RenderContext::prepareForDraw() + bool RenderContext::prepareForDraw(GraphicsState* pState, GraphicsVars* pVars) { - assert(mpGraphicsState); + assert(pState); // Vao must be valid so at least primitive topology is known - assert(mpGraphicsState->getVao().get()); + assert(pState->getVao().get()); if (is_set(StateBindFlags::Vars, mBindFlags)) { // Apply the vars. Must be first because applyGraphicsVars() might cause a flush - if (mpGraphicsVars) - { - applyGraphicsVars(); - } - else + if (pVars) { - mpLowLevelData->getCommandList()->SetGraphicsRootSignature(RootSignature::getEmpty()->getApiHandle()); + if (applyGraphicsVars(pVars) == false) return false; } + else mpLowLevelData->getCommandList()->SetGraphicsRootSignature(RootSignature::getEmpty()->getApiHandle()); + mpLastBoundGraphicsVars = pVars; } #if _ENABLE_NVAPI - if (mpGraphicsState->isSinglePassStereoEnabled()) + if (pState->isSinglePassStereoEnabled()) { NvAPI_Status ret = NvAPI_D3D12_SetSinglePassStereoMode(mpLowLevelData->getCommandList(), 2, 1, false); assert(ret == NVAPI_OK); } #else - assert(mpGraphicsState->isSinglePassStereoEnabled() == false); + assert(pState->isSinglePassStereoEnabled() == false); #endif - mBindGraphicsRootSig = false; - ID3D12GraphicsCommandList* pList = mpLowLevelData->getCommandList(); - if (is_set(StateBindFlags::Topology, mBindFlags)) - { - pList->IASetPrimitiveTopology(getD3DPrimitiveTopology(mpGraphicsState->getVao()->getPrimitiveTopology())); - } - if (is_set(StateBindFlags::Vao, mBindFlags)) - { - D3D12SetVao(this, pList, mpGraphicsState->getVao().get()); - } - if (is_set(StateBindFlags::Fbo, mBindFlags)) - { - D3D12SetFbo(this, mpGraphicsState->getFbo().get()); - } - if (is_set(StateBindFlags::SamplePositions, mBindFlags)) - { - D3D12SetSamplePositions(pList, mpGraphicsState->getFbo().get()); - } - if (is_set(StateBindFlags::Viewports, mBindFlags)) - { - D3D12SetViewports(pList, &mpGraphicsState->getViewport(0)); - } - if (is_set(StateBindFlags::Scissors, mBindFlags)) - { - D3D12SetScissors(pList, &mpGraphicsState->getScissors(0)); - } - if (is_set(StateBindFlags::PipelineState, mBindFlags)) - { - pList->SetPipelineState(mpGraphicsState->getGSO(mpGraphicsVars.get())->getApiHandle()); - } - BlendState::SharedPtr blendState = mpGraphicsState->getBlendState(); - if (blendState != nullptr) - { - pList->OMSetBlendFactor(glm::value_ptr(blendState->getBlendFactor())); - } - const auto pDsState = mpGraphicsState->getDepthStencilState(); + if (is_set(StateBindFlags::Topology, mBindFlags)) pList->IASetPrimitiveTopology(getD3DPrimitiveTopology(pState->getVao()->getPrimitiveTopology())); + if (is_set(StateBindFlags::Vao, mBindFlags)) D3D12SetVao(this, pList, pState->getVao().get()); + if (is_set(StateBindFlags::Fbo, mBindFlags)) D3D12SetFbo(this, pState->getFbo().get()); + if (is_set(StateBindFlags::SamplePositions, mBindFlags)) D3D12SetSamplePositions(pList, pState->getFbo().get()); + if (is_set(StateBindFlags::Viewports, mBindFlags)) D3D12SetViewports(pList, &pState->getViewport(0)); + if (is_set(StateBindFlags::Scissors, mBindFlags)) D3D12SetScissors(pList, &pState->getScissors(0)); + if (is_set(StateBindFlags::PipelineState, mBindFlags)) pList->SetPipelineState(pState->getGSO(pVars)->getApiHandle()); + + BlendState::SharedPtr blendState = pState->getBlendState(); + if (blendState != nullptr) pList->OMSetBlendFactor(glm::value_ptr(blendState->getBlendFactor())); + + const auto pDsState = pState->getDepthStencilState(); pList->OMSetStencilRef(pDsState == nullptr ? 0 : pDsState->getStencilRef()); mCommandsPending = true; + return true; } - void RenderContext::drawInstanced(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation) + void RenderContext::drawInstanced(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation) { - prepareForDraw(); + if (prepareForDraw(pState, pVars) == false) return; mpLowLevelData->getCommandList()->DrawInstanced(vertexCount, instanceCount, startVertexLocation, startInstanceLocation); } - void RenderContext::draw(uint32_t vertexCount, uint32_t startVertexLocation) + void RenderContext::draw(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation) { - drawInstanced(vertexCount, 1, startVertexLocation, 0); + drawInstanced(pState,pVars, vertexCount, 1, startVertexLocation, 0); } - void RenderContext::drawIndexedInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation) + void RenderContext::drawIndexedInstanced(GraphicsState* pState, GraphicsVars* pVars, uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation) { - prepareForDraw(); + if (prepareForDraw(pState, pVars) == false) return; mpLowLevelData->getCommandList()->DrawIndexedInstanced(indexCount, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation); } - void RenderContext::drawIndexed(uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation) + void RenderContext::drawIndexed(GraphicsState* pState, GraphicsVars* pVars, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation) { - drawIndexedInstanced(indexCount, 1, startIndexLocation, baseVertexLocation, 0); + drawIndexedInstanced(pState, pVars, indexCount, 1, startIndexLocation, baseVertexLocation, 0); } - void RenderContext::drawIndirect(const Buffer* argBuffer, uint64_t argBufferOffset) + void drawIndirectCommon(RenderContext* pContext, const CommandListHandle& pCommandList, ID3D12CommandSignature* pCommandSig, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, const Buffer* pCountBuffer, uint64_t countBufferOffset) { - prepareForDraw(); - resourceBarrier(argBuffer, Resource::State::IndirectArg); - mpLowLevelData->getCommandList()->ExecuteIndirect(gpDrawCommandSig, 1, argBuffer->getApiHandle(), argBufferOffset, nullptr, 0); + pContext->resourceBarrier(pArgBuffer, Resource::State::IndirectArg); + if (pCountBuffer != nullptr && pCountBuffer != pArgBuffer) pContext->resourceBarrier(pCountBuffer, Resource::State::IndirectArg); + pCommandList->ExecuteIndirect(pCommandSig, maxCommandCount, pArgBuffer->getApiHandle(), argBufferOffset, (pCountBuffer != nullptr ? pCountBuffer->getApiHandle() : nullptr), countBufferOffset); } - void RenderContext::drawIndexedIndirect(const Buffer* argBuffer, uint64_t argBufferOffset) + void RenderContext::drawIndirect(GraphicsState* pState, GraphicsVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, const Buffer* pCountBuffer, uint64_t countBufferOffset) { - prepareForDraw(); - resourceBarrier(argBuffer, Resource::State::IndirectArg); - mpLowLevelData->getCommandList()->ExecuteIndirect(gpDrawIndexCommandSig, 1, argBuffer->getApiHandle(), argBufferOffset, nullptr, 0); + if (prepareForDraw(pState, pVars) == false) return; + drawIndirectCommon(this, mpLowLevelData->getCommandList(), sApiData.pDrawCommandSig, maxCommandCount, pArgBuffer, argBufferOffset, pCountBuffer, countBufferOffset); } - void RenderContext::raytrace(RtProgramVars::SharedPtr pVars, RtState::SharedPtr pState, uint32_t width, uint32_t height) + void RenderContext::drawIndexedIndirect(GraphicsState* pState, GraphicsVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, const Buffer* pCountBuffer, uint64_t countBufferOffset) { - raytrace(pVars, pState, width, height, 1); + if (prepareForDraw(pState, pVars) == false) return; + drawIndirectCommon(this, mpLowLevelData->getCommandList(), sApiData.pDrawIndexCommandSig, maxCommandCount, pArgBuffer, argBufferOffset, pCountBuffer, countBufferOffset); } void RenderContext::raytrace(RtProgramVars::SharedPtr pVars, RtState::SharedPtr pState, uint32_t width, uint32_t height, uint32_t depth) @@ -399,38 +429,10 @@ namespace Falcor pList4->DispatchRays(&raytraceDesc); } - void RenderContext::initDrawCommandSignatures() - { - //Common properties - D3D12_COMMAND_SIGNATURE_DESC sigDesc; - sigDesc.NumArgumentDescs = 1; - sigDesc.NodeMask = 0; - D3D12_INDIRECT_ARGUMENT_DESC argDesc; - - //Draw - sigDesc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS); - argDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW; - sigDesc.pArgumentDescs = &argDesc; - gpDevice->getApiHandle()->CreateCommandSignature(&sigDesc, nullptr, IID_PPV_ARGS(&gpDrawCommandSig)); - - //Draw index - sigDesc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS); - argDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; - sigDesc.pArgumentDescs = &argDesc; - gpDevice->getApiHandle()->CreateCommandSignature(&sigDesc, nullptr, IID_PPV_ARGS(&gpDrawIndexCommandSig)); - } - void RenderContext::blit(ShaderResourceView::SharedPtr pSrc, RenderTargetView::SharedPtr pDst, const uvec4& srcRect, const uvec4& dstRect, Sampler::Filter filter) { - initBlitData(); // This has to be here and can't be in the constructor. FullScreenPass will allocate some buffers which depends on the ResourceAllocator which depends on the fence inside the RenderContext. Dependencies are fun! - if (filter == Sampler::Filter::Linear) - { - gBlitData.pVars->getDefaultBlock()->setSampler(gBlitData.samplerBindLoc, 0, gBlitData.pLinearSampler); - } - else - { - gBlitData.pVars->getDefaultBlock()->setSampler(gBlitData.samplerBindLoc, 0, gBlitData.pPointSampler); - } + auto& blitData = sApiData.blitData; + blitData.pPass->getVars()->setSampler("gSampler", (filter == Sampler::Filter::Linear) ? blitData.pLinearSampler : blitData.pPointSampler); assert(pSrc->getViewInfo().arraySize == 1 && pSrc->getViewInfo().mipCount == 1); assert(pDst->getViewInfo().arraySize == 1 && pDst->getViewInfo().mipCount == 1); @@ -460,44 +462,35 @@ namespace Falcor } // Update buffer/state - if (srcRectOffset != gBlitData.prevSrcRectOffset) + if (srcRectOffset != blitData.prevSrcRectOffset) { - gBlitData.pSrcRectBuffer->setVariable(gBlitData.offsetVarOffset, srcRectOffset); - gBlitData.prevSrcRectOffset = srcRectOffset; + blitData.pSrcRectBuffer->setVariable(blitData.offsetVarOffset, srcRectOffset); + blitData.prevSrcRectOffset = srcRectOffset; } - if (srcRectScale != gBlitData.prevSrcReftScale) + if (srcRectScale != blitData.prevSrcReftScale) { - gBlitData.pSrcRectBuffer->setVariable(gBlitData.scaleVarOffset, srcRectScale); - gBlitData.prevSrcReftScale = srcRectScale; + blitData.pSrcRectBuffer->setVariable(blitData.scaleVarOffset, srcRectScale); + blitData.prevSrcReftScale = srcRectScale; } - gBlitData.pState->setViewport(0, dstViewport); - - pushGraphicsState(gBlitData.pState); - pushGraphicsVars(gBlitData.pVars); - if (pSrcTexture->getSampleCount() > 1) { - gBlitData.pPass->getProgram()->addDefine("SAMPLE_COUNT", std::to_string(pSrcTexture->getSampleCount())); + blitData.pPass->addDefine("SAMPLE_COUNT", std::to_string(pSrcTexture->getSampleCount())); } else { - gBlitData.pPass->getProgram()->removeDefine("SAMPLE_COUNT"); + blitData.pPass->removeDefine("SAMPLE_COUNT"); } - Fbo::SharedPtr pFbo = Fbo::create(); Texture::SharedPtr pSharedTex = std::const_pointer_cast(pDstTexture->shared_from_this()); - pFbo->attachColorTarget(pSharedTex, 0, pDst->getViewInfo().mostDetailedMip, pDst->getViewInfo().firstArraySlice, pDst->getViewInfo().arraySize); - gBlitData.pState->pushFbo(pFbo, false); - gBlitData.pVars->getDefaultBlock()->setSrv(gBlitData.texBindLoc, 0, pSrc); - gBlitData.pPass->execute(this); + blitData.pFbo->attachColorTarget(pSharedTex, 0, pDst->getViewInfo().mostDetailedMip, pDst->getViewInfo().firstArraySlice, pDst->getViewInfo().arraySize); + blitData.pPass->getVars()->getDefaultBlock()->setSrv(blitData.texBindLoc, 0, pSrc); + blitData.pPass->getState()->setViewport(0, dstViewport); + blitData.pPass->execute(this, blitData.pFbo, false); // Release the resources we bound - gBlitData.pVars->getDefaultBlock()->setSrv(gBlitData.texBindLoc, 0, nullptr); - gBlitData.pState->popFbo(false); - popGraphicsState(); - popGraphicsVars(); + blitData.pPass->getVars()->getDefaultBlock()->setSrv(blitData.texBindLoc, 0, nullptr); } void RenderContext::resolveSubresource(const Texture::SharedPtr& pSrc, uint32_t srcSubresource, const Texture::SharedPtr& pDst, uint32_t dstSubresource) diff --git a/Framework/Source/API/D3D12/D3D12Resource.cpp b/Source/Falcor/Core/API/D3D12/D3D12Resource.cpp similarity index 92% rename from Framework/Source/API/D3D12/D3D12Resource.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Resource.cpp index 32ca74fa1..b49f6ce41 100644 --- a/Framework/Source/API/D3D12/D3D12Resource.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Resource.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "D3D12Resource.h" #include "Utils/StringUtils.h" @@ -139,4 +139,16 @@ namespace Falcor std::wstring ws = string_2_wstring(mName); mApiHandle->SetName(ws.c_str()); } + + + SharedResourceApiHandle Resource::createSharedApiHandle() + { + ID3D12DevicePtr pDevicePtr = gpDevice->getApiHandle(); + auto s = string_2_wstring(mName); + SharedResourceApiHandle pHandle; + + HRESULT res = pDevicePtr->CreateSharedHandle(mApiHandle, 0, GENERIC_ALL, s.c_str(), &pHandle); + if (res == S_OK) return pHandle; + else return nullptr; + } } diff --git a/Framework/Source/API/D3D12/D3D12Resource.h b/Source/Falcor/Core/API/D3D12/D3D12Resource.h similarity index 95% rename from Framework/Source/API/D3D12/D3D12Resource.h rename to Source/Falcor/Core/API/D3D12/D3D12Resource.h index 5eabae227..5456f3efa 100644 --- a/Framework/Source/API/D3D12/D3D12Resource.h +++ b/Source/Falcor/Core/API/D3D12/D3D12Resource.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/Resource.h" +#include "Core/API/Resource.h" namespace Falcor { diff --git a/Source/Falcor/Core/API/D3D12/D3D12ResourceViews.cpp b/Source/Falcor/Core/API/D3D12/D3D12ResourceViews.cpp new file mode 100644 index 000000000..08c766736 --- /dev/null +++ b/Source/Falcor/Core/API/D3D12/D3D12ResourceViews.cpp @@ -0,0 +1,498 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Core/API/ResourceViews.h" +#include "Core/API/Texture.h" +#include "Core/API/Buffer.h" +#include "Core/BufferTypes/TypedBuffer.h" +#include "Core/BufferTypes/StructuredBuffer.h" +#include "Core/BufferTypes/ConstantBuffer.h" +#include "Core/API/Device.h" + +namespace Falcor +{ + template + ResourceView::~ResourceView() = default; + + template + ViewType getViewDimension(Resource::Type type, bool isArray); + + ResourceWeakPtr getEmptyTexture() + { + return ResourceWeakPtr(); + } + + D3D12_SHADER_RESOURCE_VIEW_DESC createBufferSrvDesc(const Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) + { + assert(pBuffer); + D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; + + const TypedBufferBase* pTypedBuffer = dynamic_cast(pBuffer); + const StructuredBuffer* pStructuredBuffer = dynamic_cast(pBuffer); + + uint32_t bufferElementCount = ShaderResourceView::kMaxPossible; + if (pTypedBuffer) + { + bufferElementCount = pTypedBuffer->getElementCount(); + desc.Format = getDxgiFormat(pTypedBuffer->getResourceFormat()); + } + else if (pStructuredBuffer) + { + bufferElementCount = (uint32_t)pStructuredBuffer->getElementCount(); + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.Buffer.StructureByteStride = (uint32_t)pStructuredBuffer->getElementSize(); + } + else + { + bufferElementCount = (uint32_t)pBuffer->getSize() / sizeof(float); + desc.Format = DXGI_FORMAT_R32_TYPELESS; + desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + } + + bool useDefaultCount = (elementCount == ShaderResourceView::kMaxPossible); + assert(useDefaultCount || (firstElement + elementCount) <= bufferElementCount); // Check range + desc.Buffer.FirstElement = firstElement; + desc.Buffer.NumElements = useDefaultCount ? (bufferElementCount - firstElement) : elementCount; + + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + + return desc; + } + + D3D12_SHADER_RESOURCE_VIEW_DESC createTextureSrvDesc(const Texture* pTexture, uint32_t firstArraySlice, uint32_t arraySize, uint32_t mostDetailedMip, uint32_t mipCount) + { + assert(pTexture); + D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; + + //If not depth, returns input format + ResourceFormat colorFormat = depthToColorFormat(pTexture->getFormat()); + desc.Format = getDxgiFormat(colorFormat); + + bool isTextureArray = pTexture->getArraySize() > 1; + desc.ViewDimension = getViewDimension(pTexture->getType(), isTextureArray); + + switch (pTexture->getType()) + { + case Resource::Type::Texture1D: + if (isTextureArray) + { + desc.Texture1DArray.MipLevels = mipCount; + desc.Texture1DArray.MostDetailedMip = mostDetailedMip; + desc.Texture1DArray.ArraySize = arraySize; + desc.Texture1DArray.FirstArraySlice = firstArraySlice; + } + else + { + desc.Texture1D.MipLevels = mipCount; + desc.Texture1D.MostDetailedMip = mostDetailedMip; + } + break; + case Resource::Type::Texture2D: + if (isTextureArray) + { + desc.Texture2DArray.MipLevels = mipCount; + desc.Texture2DArray.MostDetailedMip = mostDetailedMip; + desc.Texture2DArray.ArraySize = arraySize; + desc.Texture2DArray.FirstArraySlice = firstArraySlice; + } + else + { + desc.Texture2D.MipLevels = mipCount; + desc.Texture2D.MostDetailedMip = mostDetailedMip; + } + break; + case Resource::Type::Texture2DMultisample: + if (arraySize > 1) + { + desc.Texture2DMSArray.ArraySize = arraySize; + desc.Texture2DMSArray.FirstArraySlice = firstArraySlice; + } + break; + case Resource::Type::Texture3D: + assert(arraySize == 1); + desc.Texture3D.MipLevels = mipCount; + desc.Texture3D.MostDetailedMip = mostDetailedMip; + break; + case Resource::Type::TextureCube: + if (arraySize > 1) + { + desc.TextureCubeArray.First2DArrayFace = 0; + desc.TextureCubeArray.NumCubes = arraySize; + desc.TextureCubeArray.MipLevels = mipCount; + desc.TextureCubeArray.MostDetailedMip = mostDetailedMip; + } + else + { + desc.TextureCube.MipLevels = mipCount; + desc.TextureCube.MostDetailedMip = mostDetailedMip; + } + break; + default: + should_not_get_here(); + } + + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + return desc; + } + + template + DescType createDsvRtvUavDescCommon(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + DescType desc = {}; + const Texture* pTexture = dynamic_cast(pResource); + assert(pTexture); // Buffers should not get here + + desc = {}; + uint32_t arrayMultiplier = (pResource->getType() == Resource::Type::TextureCube) ? 6 : 1; + + if (arraySize == Resource::kMaxPossible) + { + arraySize = pTexture->getArraySize() - firstArraySlice; + } + + desc.ViewDimension = getViewDimension(pTexture->getType(), pTexture->getArraySize() > 1); + + switch (pResource->getType()) + { + case Resource::Type::Texture1D: + if (pTexture->getArraySize() > 1) + { + desc.Texture1DArray.ArraySize = arraySize; + desc.Texture1DArray.FirstArraySlice = firstArraySlice; + desc.Texture1DArray.MipSlice = mipLevel; + } + else + { + desc.Texture1D.MipSlice = mipLevel; + } + break; + case Resource::Type::Texture2D: + case Resource::Type::TextureCube: + if (pTexture->getArraySize() * arrayMultiplier > 1) + { + desc.Texture2DArray.ArraySize = arraySize * arrayMultiplier; + desc.Texture2DArray.FirstArraySlice = firstArraySlice * arrayMultiplier; + desc.Texture2DArray.MipSlice = mipLevel; + } + else + { + desc.Texture2D.MipSlice = mipLevel; + } + break; + default: + if (finalCall) should_not_get_here(); + } + desc.Format = getDxgiFormat(pTexture->getFormat()); + + return desc; + } + + template + DescType createDsvRtvDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + DescType desc = createDsvRtvUavDescCommon(pResource, mipLevel, firstArraySlice, arraySize); + + if (pResource->getType() == Resource::Type::Texture2DMultisample) + { + const Texture* pTexture = dynamic_cast(pResource); + if (pTexture->getArraySize() > 1) + { + desc.Texture2DMSArray.ArraySize = arraySize; + desc.Texture2DMSArray.FirstArraySlice = firstArraySlice; + } + } + + return desc; + } + + D3D12_DEPTH_STENCIL_VIEW_DESC createDsvDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + return createDsvRtvDesc(pResource, mipLevel, firstArraySlice, arraySize); + } + + D3D12_RENDER_TARGET_VIEW_DESC createRtvDesc(const Resource* pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + return createDsvRtvDesc(pResource, mipLevel, firstArraySlice, arraySize); + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC createBufferUavDesc(const Buffer* pBuffer, uint32_t firstElement, uint32_t elementCount) + { + assert(pBuffer); + D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {}; + + const TypedBufferBase* pTypedBuffer = dynamic_cast(pBuffer); + const StructuredBuffer* pStructuredBuffer = dynamic_cast(pBuffer); + + desc = {}; + uint32_t bufferElementCount = UnorderedAccessView::kMaxPossible; + if (pTypedBuffer != nullptr) + { + bufferElementCount = (uint32_t)pTypedBuffer->getElementCount(); + desc.Format = getDxgiFormat(pTypedBuffer->getResourceFormat()); + } + else if (pStructuredBuffer != nullptr) + { + bufferElementCount = (uint32_t)pStructuredBuffer->getElementCount(); + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.Buffer.StructureByteStride = (uint32_t)pStructuredBuffer->getElementSize(); + } + else + { + bufferElementCount = ((uint32_t)pBuffer->getSize() / sizeof(float)); + desc.Format = DXGI_FORMAT_R32_TYPELESS; + desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; + } + + bool useDefaultCount = (elementCount == UnorderedAccessView::kMaxPossible); + assert(useDefaultCount || (firstElement + elementCount) <= bufferElementCount); // Check range + desc.Buffer.FirstElement = firstElement; + desc.Buffer.NumElements = useDefaultCount ? bufferElementCount - firstElement : elementCount; + + desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + + return desc; + } + + ShaderResourceView::ApiHandle createSrvDescriptor(const D3D12_SHADER_RESOURCE_VIEW_DESC& desc, Resource::ApiHandle resHandle) + { + DescriptorSet::Layout layout; + layout.addRange(DescriptorSet::Type::TextureSrv, 0, 1); + ShaderResourceView::ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); + assert(handle); + gpDevice->getApiHandle()->CreateShaderResourceView(resHandle, &desc, handle->getCpuHandle(0)); + + return handle; + } + + ShaderResourceView::SharedPtr ShaderResourceView::create(ResourceWeakPtr pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_SHADER_RESOURCE_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + if(pSharedPtr) + { + const Texture* pTexture = std::dynamic_pointer_cast(pSharedPtr).get(); + desc = createTextureSrvDesc(pTexture, firstArraySlice, arraySize, mostDetailedMip, mipCount); + resHandle = pSharedPtr->getApiHandle(); + } + else + { + desc = {}; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + } + + SharedPtr pObj = SharedPtr(new ShaderResourceView(pResource, createSrvDescriptor(desc, resHandle), mostDetailedMip, mipCount, firstArraySlice, arraySize)); + return pObj; + } + + ShaderResourceView::SharedPtr ShaderResourceView::create(ResourceWeakPtr pResource, uint32_t firstElement, uint32_t elementCount) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_SHADER_RESOURCE_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + if (pSharedPtr) + { + const Buffer* pBuffer = std::dynamic_pointer_cast(pSharedPtr).get(); + desc = createBufferSrvDesc(pBuffer, firstElement, elementCount); + resHandle = pSharedPtr->getApiHandle(); + } + else + { + desc = {}; + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + } + + SharedPtr pObj = SharedPtr(new ShaderResourceView(pResource, createSrvDescriptor(desc, resHandle), firstElement, elementCount)); + return pObj; + } + + DepthStencilView::SharedPtr DepthStencilView::create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_DEPTH_STENCIL_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + if(pSharedPtr) + { + desc = createDsvDesc(pSharedPtr.get(), mipLevel, firstArraySlice, arraySize); + resHandle = pSharedPtr->getApiHandle(); + } + else + { + desc = {}; + desc.Format = DXGI_FORMAT_D16_UNORM; + desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + } + + DescriptorSet::Layout layout; + layout.addRange(DescriptorSet::Type::Dsv, 0, 1); + ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); + assert(handle); + gpDevice->getApiHandle()->CreateDepthStencilView(resHandle, &desc, handle->getCpuHandle(0)); + + return SharedPtr(new DepthStencilView(pResource, handle, mipLevel, firstArraySlice, arraySize)); + } + + UnorderedAccessView::ApiHandle createUavDescriptor(const D3D12_UNORDERED_ACCESS_VIEW_DESC& desc, Resource::ApiHandle resHandle, Resource::ApiHandle counterHandle) + { + DescriptorSet::Layout layout; + layout.addRange(DescriptorSet::Type::TextureUav, 0, 1); + UnorderedAccessView::ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); + assert(handle); + gpDevice->getApiHandle()->CreateUnorderedAccessView(resHandle, counterHandle, &desc, handle->getCpuHandle(0)); + return handle; + } + + UnorderedAccessView::SharedPtr UnorderedAccessView::create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_UNORDERED_ACCESS_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + + if(pSharedPtr != nullptr) + { + desc = createDsvRtvUavDescCommon(pSharedPtr.get(), mipLevel, firstArraySlice, arraySize); + resHandle = pSharedPtr->getApiHandle(); + } + else + { + desc = {}; + desc.Format = DXGI_FORMAT_R32_UINT; + desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + } + + return SharedPtr(new UnorderedAccessView(pResource, createUavDescriptor(desc, resHandle, nullptr), mipLevel, firstArraySlice, arraySize)); + } + + UnorderedAccessView::SharedPtr UnorderedAccessView::create(ResourceWeakPtr pResource, uint32_t firstElement, uint32_t elementCount) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_UNORDERED_ACCESS_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + Resource::ApiHandle counterHandle = nullptr; + + if (pSharedPtr != nullptr) + { + const Buffer* pBuffer = std::dynamic_pointer_cast(pSharedPtr).get(); + desc = createBufferUavDesc(pBuffer, firstElement, elementCount); + resHandle = pSharedPtr->getApiHandle(); + + StructuredBuffer::SharedConstPtr pStructuredBuffer = std::dynamic_pointer_cast(pSharedPtr); + if (pStructuredBuffer != nullptr && pStructuredBuffer->hasUAVCounter()) + { + counterHandle = pStructuredBuffer->getUAVCounter()->getApiHandle(); + } + } + else + { + desc = {}; + desc.Format = DXGI_FORMAT_R32_UINT; + desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + } + + return SharedPtr(new UnorderedAccessView(pResource, createUavDescriptor(desc, resHandle, counterHandle), firstElement, elementCount)); + } + + RenderTargetView::~RenderTargetView() = default; + + RenderTargetView::SharedPtr RenderTargetView::create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_RENDER_TARGET_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + if(pSharedPtr) + { + desc = createRtvDesc(pSharedPtr.get(), mipLevel, firstArraySlice, arraySize); + resHandle = pSharedPtr->getApiHandle(); + } + else + { + desc = {}; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;; + desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + } + + DescriptorSet::Layout layout; + layout.addRange(DescriptorSet::Type::Rtv, 0, 1); + ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); + assert(handle); + gpDevice->getApiHandle()->CreateRenderTargetView(resHandle, &desc, handle->getCpuHandle(0)); + + SharedPtr pObj = SharedPtr(new RenderTargetView(pResource, handle, mipLevel, firstArraySlice, arraySize)); + return pObj; + } + + ConstantBufferView::SharedPtr ConstantBufferView::create(ResourceWeakPtr pResource) + { + Resource::SharedConstPtr pSharedPtr = pResource.lock(); + + if (!pSharedPtr && getNullView()) return getNullView(); + + D3D12_CONSTANT_BUFFER_VIEW_DESC desc; + Resource::ApiHandle resHandle = nullptr; + if (pSharedPtr) + { + ConstantBuffer::SharedConstPtr pBuffer = std::dynamic_pointer_cast(pSharedPtr); + desc.BufferLocation = pBuffer->getGpuAddress(); + desc.SizeInBytes = (uint32_t)pBuffer->getSize(); + resHandle = pSharedPtr->getApiHandle(); + } + else + { + desc = {}; + } + + DescriptorSet::Layout layout; + layout.addRange(DescriptorSet::Type::Cbv, 0, 1); + ApiHandle handle = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); + assert(handle); + gpDevice->getApiHandle()->CreateConstantBufferView(&desc, handle->getCpuHandle(0)); + + SharedPtr pObj = SharedPtr(new ConstantBufferView(pResource, handle)); + return pObj; + } +} diff --git a/Framework/Source/API/D3D12/LowLevel/D3D12RootSignature.cpp b/Source/Falcor/Core/API/D3D12/D3D12RootSignature.cpp similarity index 95% rename from Framework/Source/API/D3D12/LowLevel/D3D12RootSignature.cpp rename to Source/Falcor/Core/API/D3D12/D3D12RootSignature.cpp index 5e91bc075..13b7e8e60 100644 --- a/Framework/Source/API/D3D12/LowLevel/D3D12RootSignature.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12RootSignature.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,10 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/RootSignature.h" -#include "API/D3D12/D3D12State.h" -#include "API/Device.h" +#include "stdafx.h" +#include "Core/API/RootSignature.h" +#include "D3D12State.h" +#include "Core/API/Device.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3D12Sampler.cpp b/Source/Falcor/Core/API/D3D12/D3D12Sampler.cpp similarity index 92% rename from Framework/Source/API/D3D12/D3D12Sampler.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Sampler.cpp index 7b11c6a11..617fb8524 100644 --- a/Framework/Source/API/D3D12/D3D12Sampler.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Sampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Sampler.h" -#include "API/D3D12/D3D12State.h" -#include "API/Device.h" -#include "Api/DescriptorSet.h" +#include "stdafx.h" +#include "Core/API/Sampler.h" +#include "D3D12State.h" +#include "Core/API/Device.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3DShader.cpp b/Source/Falcor/Core/API/D3D12/D3D12Shader.cpp similarity index 95% rename from Framework/Source/API/D3D12/D3DShader.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Shader.cpp index c67c1e158..76767b880 100644 --- a/Framework/Source/API/D3D12/D3DShader.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Shader.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include -#include "API/Shader.h" +#include "stdafx.h" +#include "Core/API/Shader.h" +#include "Slang/slang.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/D3D12State.cpp b/Source/Falcor/Core/API/D3D12/D3D12State.cpp similarity index 98% rename from Framework/Source/API/D3D12/D3D12State.cpp rename to Source/Falcor/Core/API/D3D12/D3D12State.cpp index f71dad255..5d8b1059c 100644 --- a/Framework/Source/API/D3D12/D3D12State.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12State.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "D3D12State.h" -#include "API/BlendState.h" -#include "API/RasterizerState.h" -#include "API/VertexLayout.h" +#include "Core/API/Sampler.h" #include "glm/gtc/type_ptr.hpp" namespace Falcor @@ -226,10 +224,9 @@ namespace Falcor { switch (func) { - case FalcorType::Disabled: - return D3D12_COMPARISON_FUNC_ALWAYS; case FalcorType::Never: return D3D12_COMPARISON_FUNC_NEVER; + case FalcorType::Disabled: case FalcorType::Always: return D3D12_COMPARISON_FUNC_ALWAYS; case FalcorType::Less: @@ -372,7 +369,7 @@ namespace Falcor D3D12_SHADER_VISIBILITY getShaderVisibility(ShaderVisibility visibility) { // D3D12 doesn't support a combination of flags, it's either ALL or a single stage - if (isPowerOf2(visibility) == false) + if (isPowerOf2((uint32_t)visibility) == false) { return D3D12_SHADER_VISIBILITY_ALL; } @@ -406,10 +403,12 @@ namespace Falcor switch (type) { case RootSignature::DescType::TextureSrv: + case RootSignature::DescType::RawBufferSrv: case RootSignature::DescType::TypedBufferSrv: case RootSignature::DescType::StructuredBufferSrv: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; case RootSignature::DescType::TextureUav: + case RootSignature::DescType::RawBufferUav: case RootSignature::DescType::TypedBufferUav: case RootSignature::DescType::StructuredBufferUav: return D3D12_DESCRIPTOR_RANGE_TYPE_UAV; @@ -509,4 +508,4 @@ namespace Falcor desc.PrimitiveTopologyType = getD3DPrimitiveType(gsoDesc.getPrimitiveType()); } -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3D12State.h b/Source/Falcor/Core/API/D3D12/D3D12State.h similarity index 94% rename from Framework/Source/API/D3D12/D3D12State.h rename to Source/Falcor/Core/API/D3D12/D3D12State.h index 9389d3f77..64faffe39 100644 --- a/Framework/Source/API/D3D12/D3D12State.h +++ b/Source/Falcor/Core/API/D3D12/D3D12State.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,11 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - #include -#include "API/RenderContext.h" -#include "API/GraphicsStateObject.h" -#include "API/LowLevel/RootSignature.h" +#include "Core/API/RootSignature.h" +#include "Core/API/VAO.h" +#include "Core/API/GraphicsStateObject.h" namespace Falcor { @@ -38,6 +37,7 @@ namespace Falcor class RasterizerState; class DepthStencilState; class VertexLayout; + class Sampler; // We need this because the D3D objects require a string. This ensures that once the descs are destroyed, the strings get destroyed as well struct InputLayoutDesc @@ -72,6 +72,8 @@ namespace Falcor return D3D_PRIMITIVE_TOPOLOGY_POINTLIST; case Vao::Topology::LineList: return D3D_PRIMITIVE_TOPOLOGY_LINELIST; + case Vao::Topology::LineStrip: + return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; case Vao::Topology::TriangleList: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; case Vao::Topology::TriangleStrip: diff --git a/Framework/Source/API/D3D12/D3D12Texture.cpp b/Source/Falcor/Core/API/D3D12/D3D12Texture.cpp similarity index 91% rename from Framework/Source/API/D3D12/D3D12Texture.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Texture.cpp index 8142d4f77..6e006478a 100644 --- a/Framework/Source/API/D3D12/D3D12Texture.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Texture.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Texture.h" -#include "API/Device.h" -#include "API/D3D12/D3DViews.h" -#include -#include "API/Device.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/FullScreenPass.h" -#include "Graphics/GraphicsState.h" +#include "stdafx.h" +#include "Core/API/Texture.h" +#include "Core/API/Device.h" #include "D3D12Resource.h" namespace Falcor { + template + ViewType getViewDimension(Resource::Type type, bool isArray); + template<> D3D12_UAV_DIMENSION getViewDimension(Texture::Type type, bool isTextureArray) { @@ -97,7 +94,7 @@ namespace Falcor } } - void Texture::apinit(const void* pData, bool autoGenMips) + void Texture::apiInit(const void* pData, bool autoGenMips) { D3D12_RESOURCE_DESC desc = {}; @@ -144,7 +141,8 @@ namespace Falcor pClearVal = nullptr; } - d3d_call(gpDevice->getApiHandle()->CreateCommittedResource(&kDefaultHeapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, pClearVal, IID_PPV_ARGS(&mApiHandle))); + D3D12_HEAP_FLAGS heapFlags = is_set(mBindFlags, ResourceBindFlags::Shared) ? D3D12_HEAP_FLAG_SHARED : D3D12_HEAP_FLAG_NONE; + d3d_call(gpDevice->getApiHandle()->CreateCommittedResource(&kDefaultHeapProps, heapFlags, &desc, D3D12_RESOURCE_STATE_COMMON, pClearVal, IID_PPV_ARGS(&mApiHandle))); if (pData) { diff --git a/Framework/Source/API/D3D12/D3D12Vao.cpp b/Source/Falcor/Core/API/D3D12/D3D12Vao.cpp similarity index 93% rename from Framework/Source/API/D3D12/D3D12Vao.cpp rename to Source/Falcor/Core/API/D3D12/D3D12Vao.cpp index 51d449a83..c9324ff61 100644 --- a/Framework/Source/API/D3D12/D3D12Vao.cpp +++ b/Source/Falcor/Core/API/D3D12/D3D12Vao.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/VAO.h" -#include +#include "stdafx.h" +#include "Core/API/VAO.h" namespace Falcor { diff --git a/Framework/Source/API/D3D12/FalcorD3D12.h b/Source/Falcor/Core/API/D3D12/FalcorD3D12.h similarity index 97% rename from Framework/Source/API/D3D12/FalcorD3D12.h rename to Source/Falcor/Core/API/D3D12/FalcorD3D12.h index ac0ba6a30..fb12cc35e 100644 --- a/Framework/Source/API/D3D12/FalcorD3D12.h +++ b/Source/Falcor/Core/API/D3D12/FalcorD3D12.h @@ -27,10 +27,8 @@ ***************************************************************************/ #pragma once #define NOMINMAX - #include -#include -#include "API/Formats.h" +#include "Core/API/Formats.h" #include #include #include @@ -39,7 +37,7 @@ __forceinline BOOL dxBool(bool b) { return b ? TRUE : FALSE; } -#ifdef _LOG_ENABLED +#if _LOG_ENABLED #define d3d_call(a) {HRESULT hr_ = a; if(FAILED(hr_)) { d3dTraceHR( #a, hr_); }} #else #define d3d_call(a) a @@ -47,7 +45,6 @@ __forceinline BOOL dxBool(bool b) { return b ? TRUE : FALSE; } #define GET_COM_INTERFACE(base, type, var) MAKE_SMART_COM_PTR(type); concat_strings(type, Ptr) var; d3d_call(base->QueryInterface(IID_PPV_ARGS(&var))); -#pragma comment(lib, "d3dcompiler.lib") #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d3d12.lib") @@ -226,6 +223,7 @@ namespace Falcor using FboHandle = void*; using GpuAddress = D3D12_GPU_VIRTUAL_ADDRESS; using QueryHeapHandle = ID3D12QueryHeapPtr; + using SharedResourceApiHandle = HANDLE; using GraphicsStateHandle = ID3D12PipelineStatePtr; using ComputeStateHandle = ID3D12PipelineStatePtr; @@ -246,11 +244,9 @@ namespace Falcor using BlendStateHandle = void*; using DescriptorSetApiHandle = void*; - static const uint32_t kDefaultSwapChainBuffers = 3; - inline constexpr uint32_t getMaxViewportCount() { return D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; } /*! @} */ } -#define UNSUPPORTED_IN_D3D12(msg_) {Falcor::logWarning(msg_ + std::string(" is not supported in D3D12. Ignoring call."));} \ No newline at end of file +#define UNSUPPORTED_IN_D3D12(msg_) {Falcor::logWarning(msg_ + std::string(" is not supported in D3D12. Ignoring call."));} diff --git a/Framework/Source/API/DepthStencilState.cpp b/Source/Falcor/Core/API/DepthStencilState.cpp similarity index 94% rename from Framework/Source/API/DepthStencilState.cpp rename to Source/Falcor/Core/API/DepthStencilState.cpp index 0ff01f7ba..cc2a8707f 100644 --- a/Framework/Source/API/DepthStencilState.cpp +++ b/Source/Falcor/Core/API/DepthStencilState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,8 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/DepthStencilState.h" +#include "stdafx.h" +#include "Core/API/DepthStencilState.h" namespace Falcor { @@ -83,4 +83,9 @@ namespace Falcor assert(face != Face::FrontAndBack); return (face == Face::Front) ? mDesc.mStencilFront : mDesc.mStencilBack; } + + SCRIPT_BINDING(DepthStencilState) + { + m.regClass(DepthStencilState); + } } diff --git a/Framework/Source/API/DepthStencilState.h b/Source/Falcor/Core/API/DepthStencilState.h similarity index 93% rename from Framework/Source/API/DepthStencilState.h rename to Source/Falcor/Core/API/DepthStencilState.h index c17ac1097..8056dbd38 100644 --- a/Framework/Source/API/DepthStencilState.h +++ b/Source/Falcor/Core/API/DepthStencilState.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ namespace Falcor { /** Depth-Stencil state */ - class DepthStencilState : public std::enable_shared_from_this + class dlldecl DepthStencilState : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -68,7 +68,7 @@ namespace Falcor */ struct StencilDesc { - Func func = Func::Always; ///< Stencil comparison function + Func func = Func::Disabled; ///< Stencil comparison function StencilOp stencilFailOp = StencilOp::Keep; ///< Stencil operation in case stencil test fails StencilOp depthFailOp = StencilOp::Keep; ///< Stencil operation in case stencil test passes but depth test fails StencilOp depthStencilPassOp = StencilOp::Keep; ///< Stencil operation in case stencil and depth tests pass @@ -76,13 +76,14 @@ namespace Falcor /** Depth-stencil descriptor */ - class Desc + class dlldecl Desc { public: friend class DepthStencilState; - /** Enable or disable the depth-test + + /** Enable/disable depth-test */ - Desc& setDepthTest(bool enabled) { mDepthEnabled = enabled; return *this; } + Desc& setDepthEnabled(bool enabled) { mDepthEnabled = enabled; return *this; } /** Set the depth-function */ @@ -92,9 +93,9 @@ namespace Falcor */ Desc& setDepthWriteMask(bool writeDepth) { mWriteDepth = writeDepth; return *this; } - /** Enable or disable stencil test + /** Enable/disable stencil-test */ - Desc& setStencilTest(bool enabled) { mStencilEnabled = enabled; return *this; } + Desc& setStencilEnabled(bool enabled) { mStencilEnabled = enabled; return *this; } /** Set the stencil write-mask */ @@ -124,9 +125,9 @@ namespace Falcor protected: bool mDepthEnabled = true; + bool mStencilEnabled = false; bool mWriteDepth = true; Func mDepthFunc = Func::Less; - bool mStencilEnabled = false; StencilDesc mStencilFront; StencilDesc mStencilBack; uint8_t mStencilReadMask = (uint8_t)-1; @@ -183,4 +184,4 @@ namespace Falcor DepthStencilState(const Desc& Desc) : mDesc(Desc) {} Desc mDesc; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/LowLevel/DescriptorPool.cpp b/Source/Falcor/Core/API/DescriptorPool.cpp similarity index 90% rename from Framework/Source/API/LowLevel/DescriptorPool.cpp rename to Source/Falcor/Core/API/DescriptorPool.cpp index 1464e1d97..98cb9d484 100644 --- a/Framework/Source/API/LowLevel/DescriptorPool.cpp +++ b/Source/Falcor/Core/API/DescriptorPool.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "DescriptorPool.h" namespace Falcor { - DescriptorPool::SharedPtr DescriptorPool::create(const Desc& desc, GpuFence::SharedPtr pFence) + DescriptorPool::SharedPtr DescriptorPool::create(const Desc& desc, const GpuFence::SharedPtr& pFence) { SharedPtr pThis = SharedPtr(new DescriptorPool(desc, pFence)); return pThis->apiInit() ? pThis : nullptr; } - DescriptorPool::DescriptorPool(const Desc& desc, GpuFence::SharedPtr pFence) : mDesc(desc), mpFence(pFence) {} + DescriptorPool::DescriptorPool(const Desc& desc, const GpuFence::SharedPtr& pFence) : mDesc(desc), mpFence(pFence) {} DescriptorPool::~DescriptorPool() = default; diff --git a/Framework/Source/API/LowLevel/DescriptorPool.h b/Source/Falcor/Core/API/DescriptorPool.h similarity index 90% rename from Framework/Source/API/LowLevel/DescriptorPool.h rename to Source/Falcor/Core/API/DescriptorPool.h index 340c46f17..2e38de47f 100644 --- a/Framework/Source/API/LowLevel/DescriptorPool.h +++ b/Source/Falcor/Core/API/DescriptorPool.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,18 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" #include -#include "API/LowLevel/GpuFence.h" -#include namespace Falcor { struct DescriptorPoolApiData; struct DescriptorSetApiData; - class DescriptorSet; - class DescriptorPool : public std::enable_shared_from_this + class dlldecl DescriptorPool : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -53,6 +49,8 @@ namespace Falcor { TextureSrv, TextureUav, + RawBufferSrv, + RawBufferUav, TypedBufferSrv, TypedBufferUav, Cbv, @@ -67,7 +65,7 @@ namespace Falcor static const uint32_t kTypeCount = uint32(Type::Count); - class Desc + class dlldecl Desc { public: Desc& setDescCount(Type type, uint32_t count) @@ -87,7 +85,7 @@ namespace Falcor bool mShaderVisible = false; }; - static SharedPtr create(const Desc& desc, GpuFence::SharedPtr pFence); + static SharedPtr create(const Desc& desc, const GpuFence::SharedPtr& pFence); uint32_t getDescCount(Type type) const { return mDesc.mDescCount[(uint32_t)type]; } uint32_t getTotalDescCount() const { return mDesc.mTotalDescCount; } @@ -97,7 +95,7 @@ namespace Falcor void executeDeferredReleases(); private: friend DescriptorSet; - DescriptorPool(const Desc& desc, GpuFence::SharedPtr pFence); + DescriptorPool(const Desc& desc, const GpuFence::SharedPtr & pFence); bool apiInit(); void releaseAllocation(std::shared_ptr pData); Desc mDesc; diff --git a/Framework/Source/API/DescriptorSet.cpp b/Source/Falcor/Core/API/DescriptorSet.cpp similarity index 95% rename from Framework/Source/API/DescriptorSet.cpp rename to Source/Falcor/Core/API/DescriptorSet.cpp index dbc9d4c79..7f9aa6f6e 100644 --- a/Framework/Source/API/DescriptorSet.cpp +++ b/Source/Falcor/Core/API/DescriptorSet.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "DescriptorSet.h" -#include "API/Device.h" namespace Falcor { @@ -54,4 +53,4 @@ namespace Falcor return *this; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/DescriptorSet.h b/Source/Falcor/Core/API/DescriptorSet.h similarity index 93% rename from Framework/Source/API/DescriptorSet.h rename to Source/Falcor/Core/API/DescriptorSet.h index e2c5d472b..31f1949be 100644 --- a/Framework/Source/API/DescriptorSet.h +++ b/Source/Falcor/Core/API/DescriptorSet.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,16 +26,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "LowLevel/DescriptorPool.h" -#include "ResourceViews.h" -#include "Sampler.h" +#include "DescriptorPool.h" namespace Falcor { - struct DescriptorSetApiData; + class ShaderResourceView; + class UnorderedAccessView; + class ConstantBufferView; + class Sampler; class CopyContext; class RootSignature; - class Buffer; enum class ShaderVisibility { @@ -53,7 +53,7 @@ namespace Falcor enum_class_operators(ShaderVisibility); - class DescriptorSet + class dlldecl DescriptorSet { public: using SharedPtr = std::shared_ptr; @@ -98,7 +98,7 @@ namespace Falcor void setSrv(uint32_t rangeIndex, uint32_t descIndex, const ShaderResourceView* pSrv); void setUav(uint32_t rangeIndex, uint32_t descIndex, const UnorderedAccessView* pUav); void setSampler(uint32_t rangeIndex, uint32_t descIndex, const Sampler* pSampler); - void setCbv(uint32_t rangeIndex, uint32_t descIndex, const ConstantBufferView::SharedPtr& pView); + void setCbv(uint32_t rangeIndex, uint32_t descIndex, ConstantBufferView* pView); void bindForGraphics(CopyContext* pCtx, const RootSignature* pRootSig, uint32_t rootIndex); void bindForCompute(CopyContext* pCtx, const RootSignature* pRootSig, uint32_t rootIndex); @@ -112,4 +112,4 @@ namespace Falcor DescriptorPool::SharedPtr mpPool; ApiHandle mApiHandle = {}; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/Device.cpp b/Source/Falcor/Core/API/Device.cpp similarity index 75% rename from Framework/Source/API/Device.cpp rename to Source/Falcor/Core/API/Device.cpp index 1aaa5a72d..5c4d92b20 100644 --- a/Framework/Source/API/Device.cpp +++ b/Source/Falcor/Core/API/Device.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Device.h" -#include "VR/OpenVR/VRSystem.h" +#include "stdafx.h" +#include "Device.h" namespace Falcor { + void createNullViews(); + void releaseNullViews(); + + Device::SharedPtr gpDevice; + Device::SharedPtr Device::create(Window::SharedPtr& pWindow, const Device::Desc& desc) { if (gpDevice) @@ -38,18 +42,16 @@ namespace Falcor logError("Falcor only supports a single device"); return nullptr; } - gpDevice = SharedPtr(new Device(pWindow)); - if (gpDevice->init(desc) == false) { gpDevice = nullptr;} + gpDevice = SharedPtr(new Device(pWindow, desc)); + if (gpDevice->init() == false) { gpDevice = nullptr;} return gpDevice; } - bool Device::init(const Desc& desc) + bool Device::init() { - if (desc.enableVR) VRSystem::start(desc.enableVsync); - const uint32_t kDirectQueueIndex = (uint32_t)LowLevelContextData::CommandQueueType::Direct; - assert(desc.cmdQueues[kDirectQueueIndex] > 0); - if (apiInit(desc) == false) return false; + assert(mDesc.cmdQueues[kDirectQueueIndex] > 0); + if (apiInit() == false) return false; // Create the descriptor pools DescriptorPool::Desc poolDesc; @@ -58,27 +60,30 @@ namespace Falcor poolDesc.setDescCount(DescriptorPool::Type::TextureSrv, 1000000).setDescCount(DescriptorPool::Type::Sampler, 2048).setShaderVisible(true); #ifndef FALCOR_D3D12 poolDesc.setDescCount(DescriptorPool::Type::Cbv, 16 * 1024).setDescCount(DescriptorPool::Type::TextureUav, 16 * 1024); - poolDesc.setDescCount(DescriptorPool::Type::StructuredBufferSrv, 2 * 1024).setDescCount(DescriptorPool::Type::StructuredBufferUav, 2 * 1024).setDescCount(DescriptorPool::Type::TypedBufferSrv, 2 * 1024).setDescCount(DescriptorPool::Type::TypedBufferUav, 2 * 1024); + poolDesc.setDescCount(DescriptorPool::Type::StructuredBufferSrv, 2 * 1024) + .setDescCount(DescriptorPool::Type::StructuredBufferUav, 2 * 1024) + .setDescCount(DescriptorPool::Type::TypedBufferSrv, 2 * 1024) + .setDescCount(DescriptorPool::Type::TypedBufferUav, 2 * 1024) + .setDescCount(DescriptorPool::Type::RawBufferSrv, 2 * 1024) + .setDescCount(DescriptorPool::Type::RawBufferUav, 2 * 1024); #endif - mpGpuDescPool = DescriptorPool::create(poolDesc, mpRenderContext->getLowLevelData()->getFence()); + mpFrameFence = GpuFence::create(); + mpGpuDescPool = DescriptorPool::create(poolDesc, mpFrameFence); poolDesc.setShaderVisible(false).setDescCount(DescriptorPool::Type::Rtv, 16 * 1024).setDescCount(DescriptorPool::Type::Dsv, 1024); - mpCpuDescPool = DescriptorPool::create(poolDesc, mpRenderContext->getLowLevelData()->getFence()); - - if (mpRenderContext) mpRenderContext->flush(); // This will bind the descriptor heaps - - mVsyncOn = desc.enableVsync; + mpCpuDescPool = DescriptorPool::create(poolDesc, mpFrameFence); - mpResourceAllocator = ResourceAllocator::create(1024 * 1024 * 2, mpRenderContext->getLowLevelData()->getFence()); + mpUploadHeap = GpuMemoryHeap::create(GpuMemoryHeap::Type::Upload, 1024 * 1024 * 2, mpFrameFence); + createNullViews(); + mpRenderContext = RenderContext::create(mCmdQueues[(uint32_t)LowLevelContextData::CommandQueueType::Direct][0]); - mpFrameFence = GpuFence::create(); + if (mpRenderContext) mpRenderContext->flush(); // This will bind the descriptor heaps // Update the FBOs - if (updateDefaultFBO(mpWindow->getClientAreaWidth(), mpWindow->getClientAreaHeight(), desc.colorFormat, desc.depthFormat) == false) + if (updateDefaultFBO(mpWindow->getClientAreaSize().x, mpWindow->getClientAreaSize().y, mDesc.colorFormat, mDesc.depthFormat) == false) { return false; } - if (desc.enableVR && VRSystem::instance()) VRSystem::instance()->initDisplayAndController(); gpDevice->mTimestampQueryHeap = QueryHeap::create(QueryHeap::Type::Timestamp, 128 * 1024 * 1024); return true; @@ -87,10 +92,10 @@ namespace Falcor void Device::releaseFboData() { // First, delete all FBOs - for (uint32_t i = 0; i < mSwapChainBufferCount; i++) + for (auto& pFbo : mpSwapChainFbos) { - mpSwapChainFbos[i]->attachColorTarget(nullptr, 0); - mpSwapChainFbos[i]->attachDepthStencilTarget(nullptr); + pFbo->attachColorTarget(nullptr, 0); + pFbo->attachDepthStencilTarget(nullptr); } // Now execute all deferred releases @@ -99,23 +104,16 @@ namespace Falcor bool Device::updateDefaultFBO(uint32_t width, uint32_t height, ResourceFormat colorFormat, ResourceFormat depthFormat) { - mpSwapChainFbos.resize(mSwapChainBufferCount); - - std::vector apiHandles(mSwapChainBufferCount); + ResourceHandle apiHandles[kSwapChainBuffersCount] = {}; getApiFboData(width, height, colorFormat, depthFormat, apiHandles, mCurrentBackBufferIndex); - for (uint32_t i = 0; i < mSwapChainBufferCount; i++) + for (uint32_t i = 0; i < kSwapChainBuffersCount; i++) { // Create a texture object auto pColorTex = Texture::SharedPtr(new Texture(width, height, 1, 1, 1, 1, colorFormat, Texture::Type::Texture2D, Texture::BindFlags::RenderTarget)); pColorTex->mApiHandle = apiHandles[i]; - // Create the FBO if it's required - if (mpSwapChainFbos[i] == nullptr) - { - mpSwapChainFbos[i] = Fbo::create(); - } - + if (mpSwapChainFbos[i] == nullptr) mpSwapChainFbos[i] = Fbo::create(); mpSwapChainFbos[i]->attachColorTarget(pColorTex, 0); // Create a depth texture @@ -152,7 +150,7 @@ namespace Falcor void Device::executeDeferredReleases() { - mpResourceAllocator->executeDeferredReleases(); + mpUploadHeap->executeDeferredReleases(); uint64_t gpuVal = mpFrameFence->getGpuValue(); while (mDeferredReleases.size() && mDeferredReleases.front().frameID <= gpuVal) { @@ -164,7 +162,7 @@ namespace Falcor void Device::toggleVSync(bool enable) { - mVsyncOn = enable; + mDesc.enableVsync = enable; } void Device::cleanup() @@ -172,17 +170,12 @@ namespace Falcor toggleFullScreen(false); mpRenderContext->flush(true); // Release all the bound resources. Need to do that before deleting the RenderContext - mpRenderContext->setGraphicsState(nullptr); - mpRenderContext->setGraphicsVars(nullptr); - mpRenderContext->setComputeState(nullptr); - mpRenderContext->setComputeVars(nullptr); - for (uint32_t i = 0; i < arraysize(mCmdQueues); i++) mCmdQueues[i].clear(); - for (uint32_t i = 0; i < mSwapChainBufferCount; i++) mpSwapChainFbos[i].reset(); + for (uint32_t i = 0; i < kSwapChainBuffersCount; i++) mpSwapChainFbos[i].reset(); mDeferredReleases = decltype(mDeferredReleases)(); - + releaseNullViews(); mpRenderContext.reset(); - mpResourceAllocator.reset(); + mpUploadHeap.reset(); mpCpuDescPool.reset(); mpGpuDescPool.reset(); mpFrameFence.reset(); @@ -197,6 +190,7 @@ namespace Falcor mpRenderContext->flush(); apiPresent(); mpFrameFence->gpuSignal(mpRenderContext->getLowLevelData()->getCommandQueue()); + if (mpFrameFence->getCpuValue() >= kSwapChainBuffersCount) mpFrameFence->syncCpu(mpFrameFence->getCpuValue() - kSwapChainBuffersCount); executeDeferredReleases(); mFrameID++; } @@ -224,9 +218,9 @@ namespace Falcor #ifdef FALCOR_D3D12 // Save FBO resource states - std::vector fboColorStates(mSwapChainBufferCount, Resource::State::Undefined); - std::vector fboDepthStates(mSwapChainBufferCount, Resource::State::Undefined); - for (uint32_t i = 0; i < mSwapChainBufferCount; i++) + std::array fboColorStates; + std::array fboDepthStates; + for (uint32_t i = 0; i < kSwapChainBuffersCount; i++) { assert(mpSwapChainFbos[i]->getColorTexture(0)->isStateGlobal()); fboColorStates[i] = mpSwapChainFbos[i]->getColorTexture(0)->getGlobalState(); @@ -249,7 +243,7 @@ namespace Falcor #ifdef FALCOR_D3D12 // Restore FBO resource states - for (uint32_t i = 0; i < mSwapChainBufferCount; i++) + for (uint32_t i = 0; i < kSwapChainBuffersCount; i++) { assert(mpSwapChainFbos[i]->getColorTexture(0)->isStateGlobal()); mpSwapChainFbos[i]->getColorTexture(0)->setGlobalState(fboColorStates[i]); @@ -268,4 +262,13 @@ namespace Falcor return getSwapChainFbo(); } -} \ No newline at end of file + + SCRIPT_BINDING(Device) + { + auto deviceDesc = m.class_("DeviceDesc"); +#define desc_field(f_) rwField(#f_, &Device::Desc::f_) + deviceDesc.desc_field(colorFormat).desc_field(depthFormat).desc_field(apiMajorVersion).desc_field(apiMinorVersion); + deviceDesc.desc_field(enableVsync).desc_field(enableDebugLayer).desc_field(cmdQueues); +#undef desc_field + } +} diff --git a/Framework/Source/API/Device.h b/Source/Falcor/Core/API/Device.h similarity index 81% rename from Framework/Source/API/Device.h rename to Source/Falcor/Core/API/Device.h index ef2bb1497..e8d442e6f 100644 --- a/Framework/Source/API/Device.h +++ b/Source/Falcor/Core/API/Device.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,13 +26,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/Window.h" -#include "API/Texture.h" -#include "API/FBO.h" -#include "API/RenderContext.h" -#include "API/LowLevel/DescriptorPool.h" -#include "API/LowLevel/ResourceAllocator.h" -#include "API/QueryHeap.h" +#include "Core/Window.h" +#include "Core/API/Texture.h" +#include "Core/API/FBO.h" +#include "Core/API/RenderContext.h" +#include "Core/API/DescriptorPool.h" +#include "Core/API/GpuMemoryHeap.h" +#include "Core/API/QueryHeap.h" namespace Falcor { @@ -44,7 +44,7 @@ namespace Falcor struct DeviceApiData; - class Device + class dlldecl Device { public: using SharedPtr = std::shared_ptr; @@ -54,19 +54,17 @@ namespace Falcor /** Device configuration */ - struct Desc + struct Desc : ScriptBindings::enable_to_string { ResourceFormat colorFormat = ResourceFormat::BGRA8UnormSrgb; ///< The color buffer format ResourceFormat depthFormat = ResourceFormat::D32Float; ///< The depth buffer format uint32_t apiMajorVersion = 0; ///< Requested API major version. If specified, device creation will fail if not supported. Otherwise, the highest supported version will be automatically selected. uint32_t apiMinorVersion = 0; ///< Requested API minor version. If specified, device creation will fail if not supported. Otherwise, the highest supported version will be automatically selected. - std::vector requiredExtensions; ///< Extensions required by the sample bool enableVsync = false; ///< Controls vertical-sync bool enableDebugLayer = DEFAULT_ENABLE_DEBUG_LAYER; ///< Enable the debug layer. The default for release build is false, for debug build it's true. - bool enableVR = false; ///< Create a device matching OpenVR requirements static_assert((uint32_t)LowLevelContextData::CommandQueueType::Direct == 2, "Default initialization of cmdQueues assumes that Direct queue index is 2"); - uint32_t cmdQueues[kQueueTypeCount] = { 0, 0, 1 }; ///< Command queues to create. If not direct-queues are created, mpRenderContext will not be initialized + std::array cmdQueues = { 0, 0, 1 }; ///< Command queues to create. If no direct-queues are created, mpRenderContext will not be initialized #ifdef FALCOR_D3D12 // GUID list for experimental features @@ -102,11 +100,6 @@ namespace Falcor */ bool isWindowOccluded() const; - /** Check if the device support an extension - */ - deprecate("3.3", "This no longer does anything. Use isFeatureSupported() for extension and feature support queries.") - bool isExtensionSupported(const std::string & name) const; - /** Get the FBO object associated with the swap-chain. This can change each frame, depending on the API used */ @@ -139,16 +132,20 @@ namespace Falcor /** Check if vertical sync is enabled */ - bool isVsyncEnabled() const { return mVsyncOn; } + bool isVsyncEnabled() const { return mDesc.enableVsync; } /** Resize the swap-chain \return A new FBO object */ Fbo::SharedPtr resizeSwapChain(uint32_t width, uint32_t height); + /** Get the desc + */ + const Desc& getDesc() const { return mDesc; } + const DescriptorPool::SharedPtr& getCpuDescriptorPool() const { return mpCpuDescPool; } const DescriptorPool::SharedPtr& getGpuDescriptorPool() const { return mpGpuDescPool; } - const ResourceAllocator::SharedPtr& getResourceAllocator() const { return mpResourceAllocator; } + const GpuMemoryHeap::SharedPtr& getUploadHeap() const { return mpUploadHeap; } const QueryHeap::SharedPtr& getTimestampQueryHeap() const { return mTimestampQueryHeap; } void releaseResource(ApiObjectHandle pResource); double getGpuTimestampFrequency() const { return mGpuTimestampFrequency; } // ms/tick @@ -156,20 +153,13 @@ namespace Falcor /** Check if features are supported by the device */ bool isFeatureSupported(SupportedFeatures flags) const; - #ifdef FALCOR_VK - enum class MemoryType - { - Default, - Upload, - Readback, - Count - }; - uint32_t getVkMemoryType(MemoryType falcorType, uint32_t memoryTypeBits) const; + uint32_t getVkMemoryType(GpuMemoryHeap::Type falcorType, uint32_t memoryTypeBits) const; const VkPhysicalDeviceLimits& getPhysicalDeviceLimits() const; uint32_t getDeviceVendorID() const; #endif private: + static constexpr uint32_t kSwapChainBuffersCount = 3; struct ResourceRelease { size_t frameID; @@ -178,17 +168,17 @@ namespace Falcor std::queue mDeferredReleases; uint32_t mCurrentBackBufferIndex; - std::vector mpSwapChainFbos; - uint32_t mSwapChainBufferCount = kDefaultSwapChainBuffers; + Fbo::SharedPtr mpSwapChainFbos[kSwapChainBuffersCount]; - Device(Window::SharedPtr pWindow) : mpWindow(pWindow) {} - bool init(const Desc& desc); + Device(Window::SharedPtr pWindow, const Desc& desc) : mpWindow(pWindow), mDesc(desc) {} + bool init(); void executeDeferredReleases(); void releaseFboData(); bool updateDefaultFBO(uint32_t width, uint32_t height, ResourceFormat colorFormat, ResourceFormat depthFormat); + Desc mDesc; ApiHandle mApiHandle; - ResourceAllocator::SharedPtr mpResourceAllocator; + GpuMemoryHeap::SharedPtr mpUploadHeap; DescriptorPool::SharedPtr mpCpuDescPool; DescriptorPool::SharedPtr mpGpuDescPool; bool mIsWindowOccluded = false; @@ -197,7 +187,6 @@ namespace Falcor Window::SharedPtr mpWindow; DeviceApiData* mpApiData; RenderContext::SharedPtr mpRenderContext; - bool mVsyncOn; size_t mFrameID = 0; QueryHeap::SharedPtr mTimestampQueryHeap; double mGpuTimestampFrequency; @@ -206,16 +195,16 @@ namespace Falcor SupportedFeatures mSupportedFeatures = SupportedFeatures::None; // API specific functions - bool getApiFboData(uint32_t width, uint32_t height, ResourceFormat colorFormat, ResourceFormat depthFormat, std::vector& apiHandles, uint32_t& currentBackBufferIndex); + bool getApiFboData(uint32_t width, uint32_t height, ResourceFormat colorFormat, ResourceFormat depthFormat, ResourceHandle apiHandles[kSwapChainBuffersCount], uint32_t& currentBackBufferIndex); void destroyApiObjects(); void apiPresent(); - bool apiInit(const Desc& desc); + bool apiInit(); bool createSwapChain(ResourceFormat colorFormat); void apiResizeSwapChain(uint32_t width, uint32_t height, ResourceFormat colorFormat); void toggleFullScreen(bool fullscreen); }; - dlldecl Device::SharedPtr gpDevice; + dlldecl extern Device::SharedPtr gpDevice; enum_class_operators(Device::SupportedFeatures); } diff --git a/Source/Falcor/Core/API/FBO.cpp b/Source/Falcor/Core/API/FBO.cpp new file mode 100644 index 000000000..aacee5080 --- /dev/null +++ b/Source/Falcor/Core/API/FBO.cpp @@ -0,0 +1,473 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "FBO.h" + +namespace Falcor +{ + namespace + { + bool checkAttachmentParams(const Texture* pTexture, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize, bool isDepthAttachment) + { +#ifndef _DEBUG + return true; +#endif + if (pTexture == nullptr) + { + return true; + } + + if (mipLevel >= pTexture->getMipCount()) + { + logError("Error when attaching texture to FBO. Requested mip-level is out-of-bound."); + return false; + } + + if (arraySize != Fbo::kAttachEntireMipLevel) + { + if (arraySize == 0) + { + logError("Error when attaching texture to FBO. Requested to attach zero array slices"); + return false; + } + + if (pTexture->getType() == Texture::Type::Texture3D) + { + if (arraySize + firstArraySlice > pTexture->getDepth()) + { + logError("Error when attaching texture to FBO. Requested depth-index is out-of-bound."); + return false; + } + } + else + { + if (arraySize + firstArraySlice > pTexture->getArraySize()) + { + logError("Error when attaching texture to FBO. Requested array index is out-of-bound."); + return false; + } + } + } + + if (isDepthAttachment) + { + if (isDepthStencilFormat(pTexture->getFormat()) == false) + { + logError("Error when attaching texture to FBO. Attaching to depth-stencil target, but resource has color format."); + return false; + } + + if ((pTexture->getBindFlags() & Texture::BindFlags::DepthStencil) == Texture::BindFlags::None) + { + logError("Error when attaching texture to FBO. Attaching to depth-stencil target, the texture wasn't create with the DepthStencil bind flag"); + return false; + + } + } + else + { + if (isDepthStencilFormat(pTexture->getFormat())) + { + logError("Error when attaching texture to FBO. Attaching to color target, but resource has depth-stencil format."); + return false; + } + + if ((pTexture->getBindFlags() & Texture::BindFlags::RenderTarget) == Texture::BindFlags::None) + { + logError("Error when attaching texture to FBO. Attaching to color target, the texture wasn't create with the RenderTarget bind flag"); + return false; + + } + } + + return true; + } + + bool CheckParams(const std::string& Func, uint32_t width, uint32_t height, uint32_t arraySize, uint32_t mipLevels, uint32_t sampleCount) + { + std::string msg = "Fbo::" + Func + "() - "; + std::string param; + + if (mipLevels == 0) param = "mipLevels"; + else if (width == 0) param = "width"; + else if (height == 0) param = "height"; + else if (arraySize == 0) param = "arraySize"; + else + { + if (sampleCount > 1 && mipLevels > 1) + { + logError(msg + "can't create multi-sampled texture with more than one mip-level. sampleCount = " + std::to_string(sampleCount) + ", mipLevels = " + std::to_string(mipLevels) + "."); + return false; + } + return true; + } + + logError(msg + param + " can't be zero."); + return false; + } + + Texture::SharedPtr createTexture2D(uint32_t w, uint32_t h, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize, uint32_t mipLevels, Texture::BindFlags flags) + { + if (format == ResourceFormat::Unknown) + { + logError("Can't create Texture2D with an unknown resource format"); + return nullptr; + } + + Texture::SharedPtr pTex; + if (sampleCount > 1) + { + pTex = Texture::create2DMS(w, h, format, sampleCount, arraySize, flags); + } + else + { + pTex = Texture::create2D(w, h, format, arraySize, mipLevels, nullptr, flags); + } + + return pTex; + } + + Texture::BindFlags getBindFlags(bool isDepth, bool allowUav) + { + Texture::BindFlags flags = Texture::BindFlags::ShaderResource; + flags |= isDepth ? Texture::BindFlags::DepthStencil : Texture::BindFlags::RenderTarget; + + if (allowUav) + { + flags |= Texture::BindFlags::UnorderedAccess; + } + return flags; + } + + }; + + std::unordered_set Fbo::sDescs; + + size_t Fbo::DescHash::operator()(const Fbo::Desc& d) const + { + size_t hash = 0; + std::hash u32hash; + std::hash bhash; + for (uint32_t i = 0; i < getMaxColorTargetCount(); i++) + { + uint32_t format = (uint32_t)d.getColorTargetFormat(i); + format <<= i; + hash |= u32hash(format) >> i; + hash |= bhash(d.isColorTargetUav(i)) << i; + } + + uint32_t format = (uint32_t)d.getDepthStencilFormat(); + hash |= u32hash(format); + hash |= bhash(d.isDepthStencilUav()); + hash |= u32hash(d.getSampleCount()); + + return hash; + } + + bool Fbo::Desc::operator==(const Fbo::Desc& other) const + { + if (mColorTargets.size() != other.mColorTargets.size()) return false; + + for (size_t i = 0; i < mColorTargets.size(); i++) + { + if (mColorTargets[i] != other.mColorTargets[i]) return false; + } + if (mDepthStencilTarget != other.mDepthStencilTarget) return false; + if (mSampleCount != other.mSampleCount) return false; + + return true; + } + + Fbo::Desc::Desc() + { + mColorTargets.resize(Fbo::getMaxColorTargetCount()); + } + + Fbo::SharedPtr Fbo::create() + { + return SharedPtr(new Fbo()); + } + + Fbo::SharedPtr Fbo::create(const std::vector& colors, const Texture::SharedPtr& pDepth) + { + auto pFbo = create(); + for (uint32_t i = 0 ; i < colors.size() ; i++) + { + if (pFbo->attachColorTarget(colors[i], i) == false) return nullptr; + } + if (pDepth) + { + if (pFbo->attachDepthStencilTarget(pDepth) == false) return nullptr; + } + + return pFbo->finalize() ? pFbo : nullptr; + } + + Fbo::SharedPtr Fbo::getDefault() + { + static Fbo::SharedPtr pDefault; + if (pDefault == nullptr) + { + pDefault = Fbo::SharedPtr(new Fbo()); + } + return pDefault; + } + + bool Fbo::attachDepthStencilTarget(const Texture::SharedPtr& pDepthStencil, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + if (checkAttachmentParams(pDepthStencil.get(), mipLevel, firstArraySlice, arraySize, true) == false) return false; + + mpDesc = nullptr; + mDepthStencil.pTexture = pDepthStencil; + mDepthStencil.mipLevel = mipLevel; + mDepthStencil.firstArraySlice = firstArraySlice; + mDepthStencil.arraySize = arraySize; + bool allowUav = false; + if (pDepthStencil) + { + allowUav = ((pDepthStencil->getBindFlags() & Texture::BindFlags::UnorderedAccess) != Texture::BindFlags::None); + } + + mTempDesc.setDepthStencilTarget(pDepthStencil ? pDepthStencil->getFormat() : ResourceFormat::Unknown, allowUav); + applyDepthAttachment(); + return true; + } + + bool Fbo::attachColorTarget(const Texture::SharedPtr& pTexture, uint32_t rtIndex, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + if (rtIndex >= mColorAttachments.size()) + { + logError("Error when attaching texture to FBO. Requested color index " + std::to_string(rtIndex) + ", but context only supports " + std::to_string(mColorAttachments.size()) + " targets"); + return false; + } + + if (checkAttachmentParams(pTexture.get(), mipLevel, firstArraySlice, arraySize, false) == false) return false; + + mpDesc = nullptr; + mColorAttachments[rtIndex].pTexture = pTexture; + mColorAttachments[rtIndex].mipLevel = mipLevel; + mColorAttachments[rtIndex].firstArraySlice = firstArraySlice; + mColorAttachments[rtIndex].arraySize = arraySize; + bool allowUav = false; + if (pTexture) + { + allowUav = ((pTexture->getBindFlags() & Texture::BindFlags::UnorderedAccess) != Texture::BindFlags::None); + } + + mTempDesc.setColorTarget(rtIndex, pTexture ? pTexture->getFormat() : ResourceFormat::Unknown, allowUav); + applyColorAttachment(rtIndex); + return true; + } + + bool Fbo::verifyAttachment(const Attachment& attachment) const + { + const Texture* pTexture = attachment.pTexture.get(); + if (pTexture) + { + // Calculate size + if (mWidth == uint32_t(-1)) + { + // First attachment in the FBO + mTempDesc.setSampleCount(pTexture->getSampleCount()); + mIsLayered = (attachment.arraySize > 1); + } + + mWidth = min(mWidth, pTexture->getWidth(attachment.mipLevel)); + mHeight = min(mHeight, pTexture->getHeight(attachment.mipLevel)); + mDepth = min(mDepth, pTexture->getDepth(attachment.mipLevel)); + + { + if ( (pTexture->getSampleCount() > mTempDesc.getSampleCount()) && isDepthStencilFormat(pTexture->getFormat()) ) + { + // We're using target-independent raster (more depth samples than color samples). This is OK. + mTempDesc.setSampleCount(pTexture->getSampleCount()); + return true; + } + + if (mTempDesc.getSampleCount() != pTexture->getSampleCount()) + { + logError("Error when validating FBO. Different sample counts in attachments\n"); + return false; + } + + + if (mIsLayered != (attachment.arraySize > 1)) + { + logError("Error when validating FBO. Can't bind both layered and non-layered textures\n"); + return false; + } + } + } + return true; + } + + bool Fbo::calcAndValidateProperties() const + { + mWidth = (uint32_t)-1; + mHeight = (uint32_t)-1; + mDepth = (uint32_t)-1; + mTempDesc.setSampleCount(uint32_t(-1)); + mIsLayered = false; + + // Check color + for (const auto& attachment : mColorAttachments) + { + if (verifyAttachment(attachment) == false) + { + return false; + } + } + + // Check depth + if (verifyAttachment(mDepthStencil) == false) return false; + + // In case there are sample positions, make sure they are valid + if (mSamplePositions.size()) + { + uint32_t expectedCount = mSamplePositionsPixelCount * mTempDesc.getSampleCount(); + if (expectedCount != mSamplePositions.size()) + { + logError("Error when validating FBO. The sample-positions array-size has the wrong size.\n"); + return false; + } + } + + // Insert the attachment into the static array and initialize the address + mpDesc = &(*(sDescs.insert(mTempDesc).first)); + + return true; + } + + Texture::SharedPtr Fbo::getColorTexture(uint32_t index) const + { + if (index >= mColorAttachments.size()) + { + logError("Fbo::getColorTexture(): Index is out of range. Requested " + std::to_string(index) + " but only " + std::to_string(mColorAttachments.size()) + " color slots are available."); + return nullptr; + } + return mColorAttachments[index].pTexture; + } + + const Texture::SharedPtr& Fbo::getDepthStencilTexture() const + { + return mDepthStencil.pTexture; + } + + bool Fbo::finalize() const + { + if (mpDesc == nullptr) + { + if (calcAndValidateProperties() == false) return false; + initApiHandle(); + return true; + } + return true; + } + + void Fbo::setSamplePositions(uint32_t samplesPerPixel, uint32_t pixelCount, const SamplePosition positions[]) + { + if (positions) + { + mSamplePositions = std::vector(positions, positions + (samplesPerPixel * pixelCount)); + mSamplePositionsPixelCount = pixelCount; + } + else + { + mSamplePositionsPixelCount = 0; + mSamplePositions.clear(); + } + } + + Fbo::SharedPtr Fbo::create2D(uint32_t width, uint32_t height, const Fbo::Desc& fboDesc, uint32_t arraySize, uint32_t mipLevels) + { + uint32_t sampleCount = fboDesc.getSampleCount(); + if (CheckParams("Create2D", width, height, arraySize, mipLevels, sampleCount) == false) return nullptr; + Fbo::SharedPtr pFbo = create(); + + // create the color targets + for (uint32_t i = 0; i < Fbo::getMaxColorTargetCount(); i++) + { + if (fboDesc.getColorTargetFormat(i) != ResourceFormat::Unknown) + { + Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); + Texture::SharedPtr pTex = createTexture2D(width, height, fboDesc.getColorTargetFormat(i), sampleCount, arraySize, mipLevels, flags); + pFbo->attachColorTarget(pTex, i, 0, 0, kAttachEntireMipLevel); + } + } + + if (fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) + { + Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); + Texture::SharedPtr pDepth = createTexture2D(width, height, fboDesc.getDepthStencilFormat(), sampleCount, arraySize, mipLevels, flags); + pFbo->attachDepthStencilTarget(pDepth, 0, 0, kAttachEntireMipLevel); + } + + return pFbo; + } + + Fbo::SharedPtr Fbo::createCubemap(uint32_t width, uint32_t height, const Desc& fboDesc, uint32_t arraySize, uint32_t mipLevels) + { + if (fboDesc.getSampleCount() > 1) + { + logError("creatceCubemap() - can't create a multisampled FBO"); + return nullptr; + } + if (CheckParams("CreateCubemap", width, height, arraySize, mipLevels, 0) == false) return nullptr; + + Fbo::SharedPtr pFbo = create(); + + // create the color targets + for (uint32_t i = 0; i < getMaxColorTargetCount(); i++) + { + Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); + auto pTex = Texture::createCube(width, height, fboDesc.getColorTargetFormat(i), arraySize, mipLevels, nullptr, flags); + pFbo->attachColorTarget(pTex, i, 0, kAttachEntireMipLevel); + } + + if (fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) + { + Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); + auto pDepth = Texture::createCube(width, height, fboDesc.getDepthStencilFormat(), arraySize, mipLevels, nullptr, flags); + pFbo->attachDepthStencilTarget(pDepth, 0, kAttachEntireMipLevel); + } + + return pFbo; + } + + Fbo::SharedPtr Fbo::create2D(uint32_t width, uint32_t height, ResourceFormat color, ResourceFormat depth) + { + Desc d; + d.setColorTarget(0, color).setDepthStencilTarget(depth); + return create2D(width, height, d); + } + + SCRIPT_BINDING(Fbo) + { + m.regClass(Fbo); + } +} diff --git a/Framework/Source/API/FBO.h b/Source/Falcor/Core/API/FBO.h similarity index 73% rename from Framework/Source/API/FBO.h rename to Source/Falcor/Core/API/FBO.h index b67f73b69..e284fdeb2 100644 --- a/Framework/Source/API/FBO.h +++ b/Source/Falcor/Core/API/FBO.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,26 +26,22 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec4.hpp" -#include "Texture.h" -#include "Utils/Bitmap.h" -#include +#include "Core/API/Texture.h" +#include "Core/API/ResourceViews.h" namespace Falcor { - enum class FboAttachmentType; - /** Low level framebuffer object. This class abstracts the API's framebuffer creation and management. */ - class Fbo : public std::enable_shared_from_this + class dlldecl Fbo : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; using ApiHandle = FboHandle; - class Desc + class dlldecl Desc { public: Desc(); @@ -125,13 +121,47 @@ namespace Falcor */ static SharedPtr create(); + /** Create an FBO from a list of textures. It will bind mip 0 and the all of the array-slices + \param[in] colors A vector with color-textures. The index in the vector corresponds to the render-target index in the shader. You can use nullptr for unused indices + \param[in] depth An optional depth-buffer texture + \return A new object. The function will return nullptr if there's a mismatch in the textures can't be used together as an FBO (due to size mismatch, bind-flags issues, illegal formats, etc.) + */ + static SharedPtr create(const std::vector& colors, const Texture::SharedPtr& pDepth = nullptr); + + /** Create a color-only 2D framebuffer. + \param[in] width Width of the render-targets. + \param[in] height Height of the render-targets. + \param[in] fboDesc Struct specifying the frame buffer's attachments and formats. + \param[in] arraySize Optional. The number of array slices in the texture. + \param[in] mipLevels Optional. The number of mip levels to create. You can use Texture#kMaxPossible to create the entire chain + */ + static SharedPtr create2D(uint32_t width, uint32_t height, const Desc& fboDesc, uint32_t arraySize = 1, uint32_t mipLevels = 1); + + /** Create a color-only cubemap framebuffer. + \param[in] width width of the render-targets. + \param[in] height height of the render-targets. + \param[in] fboDesc Struct specifying the frame buffer's attachments and formats. + \param[in] arraySize Optional. The number of cubes in the texture. + \param[in] mipLevels Optional. The number of mip levels to create. You can use Texture#kMaxPossible to create the entire chain + */ + static SharedPtr createCubemap(uint32_t width, uint32_t height, const Desc& fboDesc, uint32_t arraySize = 1, uint32_t mipLevels = 1); + + /** Creates an FBO with a single color texture (single mip, single array-slice), and optionally a depth-buffer + \param[in] width Width of the render-targets + \param[in] height Height of the render-targets + \param[in] color The color format + \param[in] depth The depth-format. If a depth-buffer is not required, use ResourceFormat::Unknown + */ + static SharedPtr create2D(uint32_t width, uint32_t height, ResourceFormat color, ResourceFormat depth = ResourceFormat::Unknown); + /** Attach a depth-stencil texture. \param pDepthStencil The depth-stencil texture. \param mipLevel The selected mip-level to attach. \param firstArraySlice The first array-slice to bind \param arraySize The number of array sliced to bind, or Fbo#kAttachEntireMipLevel to attach the range [firstArraySlice, pTexture->getArraySize()] + \return false if the texture can't be used as a depth-buffer (usually a format or bind-flags issue). Otherwise, true */ - void attachDepthStencilTarget(const Texture::SharedPtr& pDepthStencil, uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kAttachEntireMipLevel); + bool attachDepthStencilTarget(const Texture::SharedPtr& pDepthStencil, uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kAttachEntireMipLevel); /** Attach a color texture. \param pColorTexture The color texture. @@ -139,8 +169,9 @@ namespace Falcor \param mipLevel The selected mip-level to attach. \param firstArraySlice The first array-slice to bind \param arraySize The number of array sliced to bind, or Fbo#kAttachEntireMipLevel to attach the range [firstArraySlice, pTexture->getArraySize()] + \return false if the texture can't be used as a color-target (usually a format or bind-flags issue). Otherwise, true */ - void attachColorTarget(const Texture::SharedPtr& pColorTexture, uint32_t rtIndex, uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kAttachEntireMipLevel); + bool attachColorTarget(const Texture::SharedPtr& pColorTexture, uint32_t rtIndex, uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kAttachEntireMipLevel); /** Get the object's API handle. */ @@ -158,29 +189,21 @@ namespace Falcor */ const Texture::SharedPtr& getDepthStencilTexture() const; - /** Validates that the framebuffer attachments are OK. This function causes the actual HW resources to be generated (RTV/DSV in DX, FBO attachment calls in GL). - */ - bool checkStatus() const; - /** Get the width of the FBO */ - uint32_t getWidth() const { checkStatus(); return mWidth; } + uint32_t getWidth() const { finalize(); return mWidth; } /** Get the height of the FBO */ - uint32_t getHeight() const { checkStatus(); return mHeight; } + uint32_t getHeight() const { finalize(); return mHeight; } /** Get the sample-count of the FBO */ - uint32_t getSampleCount() const { checkStatus(); return mpDesc->getSampleCount(); } - - /** Force the FBO to have zero attachment (no texture attached) and use a virtual resolution. - */ - void setZeroAttachments(uint32_t width, uint32_t height, uint32_t layers=1, uint32_t samples=1, bool fixedSampleLocs=false); + uint32_t getSampleCount() const { finalize(); return mpDesc->getSampleCount(); } /** Get the FBO format descriptor */ - const Desc& getDesc() const { checkStatus(); return *mpDesc; } + const Desc& getDesc() const { finalize(); return *mpDesc; } /** Get a depth-stencil view to the depth-stencil target. */ @@ -190,7 +213,6 @@ namespace Falcor */ RenderTargetView::SharedPtr getRenderTargetView(uint32_t rtIndex) const; - struct SamplePosition { int8 xOffset = 0; @@ -236,6 +258,9 @@ namespace Falcor void applyDepthAttachment(); void initApiHandle() const; + // Validates that the framebuffer attachments are OK. This function causes the actual HW resources to be generated (RTV/DSV) + bool finalize() const; + Fbo(); std::vector mColorAttachments; std::vector mSamplePositions; @@ -255,4 +280,4 @@ namespace Falcor mutable ApiHandle mApiHandle = {}; void* mpPrivateData = nullptr; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/LowLevel/FencedPool.h b/Source/Falcor/Core/API/FencedPool.h similarity index 95% rename from Framework/Source/API/LowLevel/FencedPool.h rename to Source/Falcor/Core/API/FencedPool.h index 09bc9d16d..434cb8776 100644 --- a/Framework/Source/API/LowLevel/FencedPool.h +++ b/Source/Falcor/Core/API/FencedPool.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,7 +32,7 @@ namespace Falcor { template - class FencedPool : public std::enable_shared_from_this> + class dlldecl FencedPool : public std::enable_shared_from_this> { public: using SharedPtr = std::shared_ptr>; @@ -77,4 +77,4 @@ namespace Falcor NewObjectFuncType mNewObjFunc = nullptr; std::queue mQueue; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/Formats.cpp b/Source/Falcor/Core/API/Formats.cpp similarity index 54% rename from Framework/Source/API/Formats.cpp rename to Source/Falcor/Core/API/Formats.cpp index 4bca37df6..9c04e8c92 100644 --- a/Framework/Source/API/Formats.cpp +++ b/Source/Falcor/Core/API/Formats.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,96 +25,106 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Formats.h" +#include "stdafx.h" +#include "Formats.h" namespace Falcor { const FormatDesc kFormatDesc[] = { + // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} {numChannelBits.x, numChannelBits.y, numChannelBits.z, numChannelBits.w} + {ResourceFormat::Unknown, "Unknown", 0, 0, FormatType::Unknown, {false, false, false,}, {1, 1}, {0, 0, 0, 0 }}, + {ResourceFormat::R8Unorm, "R8Unorm", 1, 1, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 0, 0, 0 }}, + {ResourceFormat::R8Snorm, "R8Snorm", 1, 1, FormatType::Snorm, {false, false, false,}, {1, 1}, {8, 0, 0, 0 }}, + {ResourceFormat::R16Unorm, "R16Unorm", 2, 1, FormatType::Unorm, {false, false, false,}, {1, 1}, {16, 0, 0, 0 }}, + {ResourceFormat::R16Snorm, "R16Snorm", 2, 1, FormatType::Snorm, {false, false, false,}, {1, 1}, {16, 0, 0, 0 }}, + {ResourceFormat::RG8Unorm, "RG8Unorm", 2, 2, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 8, 0, 0 }}, + {ResourceFormat::RG8Snorm, "RG8Snorm", 2, 2, FormatType::Snorm, {false, false, false,}, {1, 1}, {8, 8, 0, 0 }}, + {ResourceFormat::RG16Unorm, "RG16Unorm", 4, 2, FormatType::Unorm, {false, false, false,}, {1, 1}, {16, 16, 0, 0 }}, + {ResourceFormat::RG16Snorm, "RG16Snorm", 4, 2, FormatType::Snorm, {false, false, false,}, {1, 1}, {16, 16, 0, 0 }}, + {ResourceFormat::RGB16Unorm, "RGB16Unorm", 6, 3, FormatType::Unorm, {false, false, false,}, {1, 1}, {16, 16, 16, 0 }}, + {ResourceFormat::RGB16Snorm, "RGB16Snorm", 6, 3, FormatType::Snorm, {false, false, false,}, {1, 1}, {16, 16, 16, 0 }}, + {ResourceFormat::R24UnormX8, "R24UnormX8", 4, 2, FormatType::Unorm, {false, false, false,}, {1, 1}, {24, 8, 0, 0 }}, + {ResourceFormat::RGB5A1Unorm, "RGB5A1Unorm", 2, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {5, 5, 5, 1 }}, + {ResourceFormat::RGBA8Unorm, "RGBA8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::RGBA8Snorm, "RGBA8Snorm", 4, 4, FormatType::Snorm, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::RGB10A2Unorm, "RGB10A2Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {10, 10, 10, 2 }}, + {ResourceFormat::RGB10A2Uint, "RGB10A2Uint", 4, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {10, 10, 10, 2 }}, + {ResourceFormat::RGBA16Unorm, "RGBA16Unorm", 8, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, + {ResourceFormat::RGBA8UnormSrgb, "RGBA8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} - {ResourceFormat::Unknown, "Unknown", 0, 0, FormatType::Unknown, {false, false, false,}, {1, 1}}, - {ResourceFormat::R8Unorm, "R8Unorm", 1, 1, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::R8Snorm, "R8Snorm", 1, 1, FormatType::Snorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::R16Unorm, "R16Unorm", 2, 1, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::R16Snorm, "R16Snorm", 2, 1, FormatType::Snorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG8Unorm, "RG8Unorm", 2, 2, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG8Snorm, "RG8Snorm", 2, 2, FormatType::Snorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG16Unorm, "RG16Unorm", 4, 2, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG16Snorm, "RG16Snorm", 4, 2, FormatType::Snorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB16Unorm, "RGB16Unorm", 6, 3, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB16Snorm, "RGB16Snorm", 6, 3, FormatType::Snorm, {false, false, false,}, {1, 1}}, - { ResourceFormat::R24UnormX8, "R24UnormX8", 4, 1, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB5A1Unorm, "RGB5A1Unorm", 2, 4, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA8Unorm, "RGBA8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA8Snorm, "RGBA8Snorm", 4, 4, FormatType::Snorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB10A2Unorm, "RGB10A2Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB10A2Uint, "RGB10A2Uint", 4, 4, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA16Unorm, "RGBA16Unorm", 8, 4, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA8UnormSrgb, "RGBA8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}}, + {ResourceFormat::R16Float, "R16Float", 2, 1, FormatType::Float, {false, false, false,}, {1, 1}, {16, 0, 0, 0 }}, + {ResourceFormat::RG16Float, "RG16Float", 4, 2, FormatType::Float, {false, false, false,}, {1, 1}, {16, 16, 0, 0 }}, + {ResourceFormat::RGB16Float, "RGB16Float", 6, 3, FormatType::Float, {false, false, false,}, {1, 1}, {16, 16, 16, 0 }}, + {ResourceFormat::RGBA16Float, "RGBA16Float", 8, 4, FormatType::Float, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, + {ResourceFormat::R32Float, "R32Float", 4, 1, FormatType::Float, {false, false, false,}, {1, 1}, {32, 0, 0, 0 }}, + {ResourceFormat::R32FloatX32, "R32FloatX32", 8, 2, FormatType::Float, {false, false, false,}, {1, 1}, {32, 32, 0, 0 }}, + {ResourceFormat::RG32Float, "RG32Float", 8, 2, FormatType::Float, {false, false, false,}, {1, 1}, {32, 32, 0, 0 }}, + {ResourceFormat::RGB32Float, "RGB32Float", 12, 3, FormatType::Float, {false, false, false,}, {1, 1}, {32, 32, 32, 0 }}, + {ResourceFormat::RGBA32Float, "RGBA32Float", 16, 4, FormatType::Float, {false, false, false,}, {1, 1}, {32, 32, 32, 32}}, + {ResourceFormat::R11G11B10Float, "R11G11B10Float", 4, 3, FormatType::Float, {false, false, false,}, {1, 1}, {11, 11, 10, 0 }}, + {ResourceFormat::RGB9E5Float, "RGB9E5Float", 4, 3, FormatType::Float, {false, false, false,}, {1, 1}, {9, 9, 9, 5 }}, + {ResourceFormat::R8Int, "R8Int", 1, 1, FormatType::Sint, {false, false, false,}, {1, 1}, {8, 0, 0, 0 }}, + {ResourceFormat::R8Uint, "R8Uint", 1, 1, FormatType::Uint, {false, false, false,}, {1, 1}, {8, 0, 0, 0 }}, + {ResourceFormat::R16Int, "R16Int", 2, 1, FormatType::Sint, {false, false, false,}, {1, 1}, {16, 0, 0, 0 }}, + {ResourceFormat::R16Uint, "R16Uint", 2, 1, FormatType::Uint, {false, false, false,}, {1, 1}, {16, 0, 0, 0 }}, + {ResourceFormat::R32Int, "R32Int", 4, 1, FormatType::Sint, {false, false, false,}, {1, 1}, {32, 0, 0, 0 }}, + {ResourceFormat::R32Uint, "R32Uint", 4, 1, FormatType::Uint, {false, false, false,}, {1, 1}, {32, 0, 0, 0 }}, + {ResourceFormat::RG8Int, "RG8Int", 2, 2, FormatType::Sint, {false, false, false,}, {1, 1}, {8, 8, 0, 0 }}, + {ResourceFormat::RG8Uint, "RG8Uint", 2, 2, FormatType::Uint, {false, false, false,}, {1, 1}, {8, 8, 0, 0 }}, + {ResourceFormat::RG16Int, "RG16Int", 4, 2, FormatType::Sint, {false, false, false,}, {1, 1}, {16, 16, 0, 0 }}, + {ResourceFormat::RG16Uint, "RG16Uint", 4, 2, FormatType::Uint, {false, false, false,}, {1, 1}, {16, 16, 0, 0 }}, + {ResourceFormat::RG32Int, "RG32Int", 8, 2, FormatType::Sint, {false, false, false,}, {1, 1}, {32, 32, 0, 0 }}, + {ResourceFormat::RG32Uint, "RG32Uint", 8, 2, FormatType::Uint, {false, false, false,}, {1, 1}, {32, 32, 0, 0 }}, // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} - {ResourceFormat::R16Float, "R16Float", 2, 1, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG16Float, "RG16Float", 4, 2, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB16Float, "RGB16Float", 6, 3, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA16Float, "RGBA16Float", 8, 4, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::R32Float, "R32Float", 4, 1, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::R32FloatX32, "R32FloatX32", 4, 1, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG32Float, "RG32Float", 8, 2, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB32Float, "RGB32Float", 12, 3, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA32Float, "RGBA32Float", 16, 4, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::R11G11B10Float, "R11G11B10Float", 4, 3, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB9E5Float, "RGB9E5Float", 4, 3, FormatType::Float, {false, false, false,}, {1, 1}}, - {ResourceFormat::R8Int, "R8Int", 1, 1, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::R8Uint, "R8Uint", 1, 1, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::R16Int, "R16Int", 2, 1, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::R16Uint, "R16Uint", 2, 1, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::R32Int, "R32Int", 4, 1, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::R32Uint, "R32Uint", 4, 1, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG8Int, "RG8Int", 2, 2, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG8Uint, "RG8Uint", 2, 2, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG16Int, "RG16Int", 4, 2, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG16Uint, "RG16Uint", 4, 2, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG32Int, "RG32Int", 8, 2, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RG32Uint, "RG32Uint", 8, 2, FormatType::Uint, {false, false, false,}, {1, 1}}, + {ResourceFormat::RGB16Int, "RGB16Int", 6, 3, FormatType::Sint, {false, false, false,}, {1, 1}, {16, 16, 16, 0 }}, + {ResourceFormat::RGB16Uint, "RGB16Uint", 6, 3, FormatType::Uint, {false, false, false,}, {1, 1}, {16, 16, 16, 0 }}, + {ResourceFormat::RGB32Int, "RGB32Int", 12, 3, FormatType::Sint, {false, false, false,}, {1, 1}, {32, 32, 32, 0 }}, + {ResourceFormat::RGB32Uint, "RGB32Uint", 12, 3, FormatType::Uint, {false, false, false,}, {1, 1}, {32, 32, 32, 0 }}, + {ResourceFormat::RGBA8Int, "RGBA8Int", 4, 4, FormatType::Sint, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::RGBA8Uint, "RGBA8Uint", 4, 4, FormatType::Uint, {false, false, false, }, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::RGBA16Int, "RGBA16Int", 8, 4, FormatType::Sint, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, + {ResourceFormat::RGBA16Uint, "RGBA16Uint", 8, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {16, 16, 16, 16}}, + {ResourceFormat::RGBA32Int, "RGBA32Int", 16, 4, FormatType::Sint, {false, false, false,}, {1, 1}, {32, 32, 32, 32}}, + {ResourceFormat::RGBA32Uint, "RGBA32Uint", 16, 4, FormatType::Uint, {false, false, false,}, {1, 1}, {32, 32, 32, 32}}, + {ResourceFormat::BGRA8Unorm, "BGRA8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::BGRA8UnormSrgb, "BGRA8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::BGRX8Unorm, "BGRX8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::BGRX8UnormSrgb, "BGRX8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}, {8, 8, 8, 8 }}, + {ResourceFormat::Alpha8Unorm, "Alpha8Unorm", 1, 1, FormatType::Unorm, {false, false, false,}, {1, 1}, {0, 0, 0, 8 }}, + {ResourceFormat::Alpha32Float, "Alpha32Float", 4, 1, FormatType::Float, {false, false, false,}, {1, 1}, {0, 0, 0, 32 }}, // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} - {ResourceFormat::RGB16Int, "RGB16Int", 6, 3, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB16Uint, "RGB16Uint", 6, 3, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB32Int, "RGB32Int", 12, 3, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGB32Uint, "RGB32Uint", 12, 3, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA8Int, "RGBA8Int", 4, 4, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA8Uint, "RGBA8Uint", 4, 4, FormatType::Uint, {false, false, false, }, {1, 1}}, - {ResourceFormat::RGBA16Int, "RGBA16Int", 8, 4, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA16Uint, "RGBA16Uint", 8, 4, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA32Int, "RGBA32Int", 16, 4, FormatType::Sint, {false, false, false,}, {1, 1}}, - {ResourceFormat::RGBA32Uint, "RGBA32Uint", 16, 4, FormatType::Uint, {false, false, false,}, {1, 1}}, - {ResourceFormat::BGRA8Unorm, "BGRA8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::BGRA8UnormSrgb, "BGRA8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}}, - {ResourceFormat::BGRX8Unorm, "BGRX8Unorm", 4, 4, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::BGRX8UnormSrgb, "BGRX8UnormSrgb", 4, 4, FormatType::UnormSrgb, {false, false, false,}, {1, 1}}, - {ResourceFormat::Alpha8Unorm, "Alpha8Unorm", 1, 1, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::Alpha32Float, "Alpha32Float", 4, 1, FormatType::Float, {false, false, false,}, {1, 1}}, - // Format Name, BytesPerBlock ChannelCount Type {bDepth, bStencil, bCompressed}, {CompressionRatio.Width, CompressionRatio.Height} - {ResourceFormat::R5G6B5Unorm, "R5G6B5Unorm", 2, 3, FormatType::Unorm, {false, false, false,}, {1, 1}}, - {ResourceFormat::D32Float, "D32Float", 4, 1, FormatType::Float, {true, false, false,}, {1, 1}}, - {ResourceFormat::D16Unorm, "D16Unorm", 2, 1, FormatType::Unorm, {true, false, false,}, {1, 1}}, - {ResourceFormat::D32FloatS8X24, "D32FloatS8X24", 8, 2, FormatType::Float, {true, true, false,}, {1, 1}}, - {ResourceFormat::D24UnormS8, "D24UnormS8", 4, 2, FormatType::Unorm, {true, true, false,}, {1, 1}}, - {ResourceFormat::BC1Unorm, "BC1Unorm", 8, 3, FormatType::Unorm, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC1UnormSrgb, "BC1UnormSrgb", 8, 3, FormatType::UnormSrgb, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC2Unorm, "BC2Unorm", 16, 4, FormatType::Unorm, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC2UnormSrgb, "BC2UnormSrgb", 16, 4, FormatType::UnormSrgb, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC3Unorm, "BC3Unorm", 16, 4, FormatType::Unorm, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC3UnormSrgb, "BC3UnormSrgb", 16, 4, FormatType::UnormSrgb, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC4Unorm, "BC4Unorm", 8, 1, FormatType::Unorm, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC4Snorm, "BC4Snorm", 8, 1, FormatType::Snorm, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC5Unorm, "BC5Unorm", 16, 2, FormatType::Unorm, {false, false, true, }, {4, 4}}, - {ResourceFormat::BC5Snorm, "BC5Snorm", 16, 2, FormatType::Snorm, {false, false, true, }, {4, 4}}, + {ResourceFormat::R5G6B5Unorm, "R5G6B5Unorm", 2, 3, FormatType::Unorm, {false, false, false,}, {1, 1}, {5, 6, 5, 0 }}, + {ResourceFormat::D32Float, "D32Float", 4, 1, FormatType::Float, {true, false, false,}, {1, 1}, {32, 0, 0, 0 }}, + {ResourceFormat::D16Unorm, "D16Unorm", 2, 1, FormatType::Unorm, {true, false, false,}, {1, 1}, {16, 0, 0, 0 }}, + {ResourceFormat::D32FloatS8X24, "D32FloatS8X24", 8, 2, FormatType::Float, {true, true, false,}, {1, 1}, {32, 8, 24, 0 }}, + {ResourceFormat::D24UnormS8, "D24UnormS8", 4, 2, FormatType::Unorm, {true, true, false,}, {1, 1}, {24, 8, 0, 0 }}, + {ResourceFormat::BC1Unorm, "BC1Unorm", 8, 3, FormatType::Unorm, {false, false, true, }, {4, 4}, {64, 0, 0, 0 }}, + {ResourceFormat::BC1UnormSrgb, "BC1UnormSrgb", 8, 3, FormatType::UnormSrgb, {false, false, true, }, {4, 4}, {64, 0, 0, 0 }}, + {ResourceFormat::BC2Unorm, "BC2Unorm", 16, 4, FormatType::Unorm, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC2UnormSrgb, "BC2UnormSrgb", 16, 4, FormatType::UnormSrgb, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC3Unorm, "BC3Unorm", 16, 4, FormatType::Unorm, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC3UnormSrgb, "BC3UnormSrgb", 16, 4, FormatType::UnormSrgb, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC4Unorm, "BC4Unorm", 8, 1, FormatType::Unorm, {false, false, true, }, {4, 4}, {64, 0, 0, 0 }}, + {ResourceFormat::BC4Snorm, "BC4Snorm", 8, 1, FormatType::Snorm, {false, false, true, }, {4, 4}, {64, 0, 0, 0 }}, + {ResourceFormat::BC5Unorm, "BC5Unorm", 16, 2, FormatType::Unorm, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC5Snorm, "BC5Snorm", 16, 2, FormatType::Snorm, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, - { ResourceFormat::BC6HS16, "BC6HS16", 16, 3, FormatType::Float, { false, false, true, }, { 4, 4 }}, - { ResourceFormat::BC6HU16, "BC6HU16", 16, 3, FormatType::Float, { false, false, true, }, { 4, 4 } }, - { ResourceFormat::BC7Unorm, "BC7Unorm", 16, 4, FormatType::Unorm, { false, false, true, }, { 4, 4 } }, - { ResourceFormat::BC7UnormSrgb, "BC7UnormSrgb", 16, 4, FormatType::UnormSrgb, { false, false, true, }, { 4, 4 } }, + {ResourceFormat::BC6HS16, "BC6HS16", 16, 3, FormatType::Float, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC6HU16, "BC6HU16", 16, 3, FormatType::Float, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC7Unorm, "BC7Unorm", 16, 4, FormatType::Unorm, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, + {ResourceFormat::BC7UnormSrgb, "BC7UnormSrgb", 16, 4, FormatType::UnormSrgb, {false, false, true, }, {4, 4}, {128, 0, 0, 0 }}, }; static_assert(arraysize(kFormatDesc) == (uint32_t)ResourceFormat::BC7UnormSrgb + 1, "Format desc table has a wrong size"); + + SCRIPT_BINDING(ResourceFormat) + { + // Resource formats + auto formats = m.enum_("ResourceFormat"); + for (uint32_t i = 0; i < (uint32_t)ResourceFormat::Count; i++) + { + formats.regEnumVal(ResourceFormat(i)); + } + } } diff --git a/Framework/Source/API/Formats.h b/Source/Falcor/Core/API/Formats.h similarity index 81% rename from Framework/Source/API/Formats.h rename to Source/Falcor/Core/API/Formats.h index 82a405d93..efb082d0b 100644 --- a/Framework/Source/API/Formats.h +++ b/Source/Falcor/Core/API/Formats.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include namespace Falcor { @@ -49,11 +48,17 @@ namespace Falcor RenderTarget = 0x40, ///< The resource will be bound as a render-target DepthStencil = 0x80, ///< The resource will be bound as a depth-stencil buffer IndirectArg = 0x100, ///< The resource will be bound as an indirect argument buffer + Shared = 0x200, ///< The resource will be shared with a different adapter. Mostly useful for sharing resoures with CUDA #ifdef FALCOR_D3D12 AccelerationStructure = 0x80000000, ///< The resource will be bound as an acceleration structure #endif + + AllColorViews = ShaderResource | UnorderedAccess | RenderTarget, + AllDepthViews = ShaderResource | DepthStencil }; + enum_class_operators(ResourceBindFlags); + /** Resource formats */ enum class ResourceFormat : uint32_t @@ -176,23 +181,24 @@ namespace Falcor uint32_t width; uint32_t height; } compressionRatio; + int numChannelBits[4]; }; - extern const FormatDesc kFormatDesc[]; + extern const dlldecl FormatDesc kFormatDesc[]; /** Get the number of bytes per format */ inline uint32_t getFormatBytesPerBlock(ResourceFormat format) { assert(kFormatDesc[(uint32_t)format].format == format); - return kFormatDesc[(uint32_t)format].bytesPerBlock; + return kFormatDesc[(uint32_t)format].bytesPerBlock; } - inline uint32_t getFormatPixelsPerBlock(ResourceFormat format) - { - assert(kFormatDesc[(uint32_t)format].format == format); - return kFormatDesc[(uint32_t)format].compressionRatio.width * kFormatDesc[(uint32_t)format].compressionRatio.height; - } + inline uint32_t getFormatPixelsPerBlock(ResourceFormat format) + { + assert(kFormatDesc[(uint32_t)format].format == format); + return kFormatDesc[(uint32_t)format].compressionRatio.width * kFormatDesc[(uint32_t)format].compressionRatio.height; + } /** Check if the format has a depth component */ @@ -257,6 +263,11 @@ namespace Falcor return kFormatDesc[(uint32_t)format].Type; } + inline uint32_t getNumChannelBits(ResourceFormat format, int channel) + { + return kFormatDesc[(uint32_t)format].numChannelBits[channel]; + } + /** Check if a format represents sRGB color space */ inline bool isSrgbFormat(ResourceFormat format) @@ -264,31 +275,31 @@ namespace Falcor return (getFormatType(format) == FormatType::UnormSrgb); } - /** Convert an SRGB format to linear. If the format is alread linear, will return it - */ - inline ResourceFormat srgbToLinearFormat(ResourceFormat format) - { - switch (format) - { - case ResourceFormat::BC1UnormSrgb: - return ResourceFormat::BC1Unorm; - case ResourceFormat::BC2UnormSrgb: - return ResourceFormat::BC2Unorm; - case ResourceFormat::BC3UnormSrgb: - return ResourceFormat::BC3Unorm; - case ResourceFormat::BGRA8UnormSrgb: - return ResourceFormat::BGRA8Unorm; - case ResourceFormat::BGRX8UnormSrgb: - return ResourceFormat::BGRX8Unorm; - case ResourceFormat::RGBA8UnormSrgb: - return ResourceFormat::RGBA8Unorm; + /** Convert an SRGB format to linear. If the format is already linear, will return it + */ + inline ResourceFormat srgbToLinearFormat(ResourceFormat format) + { + switch (format) + { + case ResourceFormat::BC1UnormSrgb: + return ResourceFormat::BC1Unorm; + case ResourceFormat::BC2UnormSrgb: + return ResourceFormat::BC2Unorm; + case ResourceFormat::BC3UnormSrgb: + return ResourceFormat::BC3Unorm; + case ResourceFormat::BGRA8UnormSrgb: + return ResourceFormat::BGRA8Unorm; + case ResourceFormat::BGRX8UnormSrgb: + return ResourceFormat::BGRX8Unorm; + case ResourceFormat::RGBA8UnormSrgb: + return ResourceFormat::RGBA8Unorm; case ResourceFormat::BC7UnormSrgb: return ResourceFormat::BC7Unorm; - default: - assert(isSrgbFormat(format) == false); - return format; - } - } + default: + assert(isSrgbFormat(format) == false); + return format; + } + } /** Convert an linear format to sRGB. If the format doesn't have a matching sRGB format, will return the original */ @@ -314,7 +325,7 @@ namespace Falcor return format; } } - + inline ResourceFormat depthToColorFormat(ResourceFormat format) { switch (format) @@ -386,5 +397,32 @@ namespace Falcor } #undef type_2_string } + + inline const std::string to_string(ResourceBindFlags flags) + { + std::string s; + if (flags == ResourceBindFlags::None) + { + return "None"; + } + +#define flag_to_str(f_) if (is_set(flags, ResourceBindFlags::f_)) (s += (s.size() ? " | " : "") + std::string(#f_)) + + flag_to_str(Vertex); + flag_to_str(Index); + flag_to_str(Constant); + flag_to_str(StreamOutput); + flag_to_str(ShaderResource); + flag_to_str(UnorderedAccess); + flag_to_str(RenderTarget); + flag_to_str(DepthStencil); + flag_to_str(IndirectArg); +#ifdef FALCOR_D3D12 + flag_to_str(AccelerationStructure); +#endif +#undef flag_to_str + + return s; + } /*! @} */ -} \ No newline at end of file +} diff --git a/Framework/Source/API/LowLevel/GpuFence.h b/Source/Falcor/Core/API/GpuFence.h similarity index 93% rename from Framework/Source/API/LowLevel/GpuFence.h rename to Source/Falcor/Core/API/GpuFence.h index 979d7eafe..430b2fefc 100644 --- a/Framework/Source/API/LowLevel/GpuFence.h +++ b/Source/Falcor/Core/API/GpuFence.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" +#include namespace Falcor { @@ -35,7 +35,7 @@ namespace Falcor /** This class can be used to synchronize GPU and CPU execution It's value monotonically increasing - every time a signal is sent, it will change the value first */ - class GpuFence : public std::enable_shared_from_this + class dlldecl GpuFence : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -65,7 +65,7 @@ namespace Falcor /** Tell the CPU to wait until the fence reaches the current value */ - void syncCpu(); + void syncCpu(std::optional val = {}); /** Insert a signal command into the command queue. This will increase the internal value */ diff --git a/Framework/Source/API/LowLevel/ResourceAllocator.cpp b/Source/Falcor/Core/API/GpuMemoryHeap.cpp similarity index 82% rename from Framework/Source/API/LowLevel/ResourceAllocator.cpp rename to Source/Falcor/Core/API/GpuMemoryHeap.cpp index 85a96db36..3c5e9bb5e 100644 --- a/Framework/Source/API/LowLevel/ResourceAllocator.cpp +++ b/Source/Falcor/Core/API/GpuMemoryHeap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,25 +25,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/ResourceAllocator.h" -#include "API/Buffer.h" +#include "stdafx.h" +#include "GpuMemoryHeap.h" +#include "GpuFence.h" namespace Falcor { - ResourceAllocator::~ResourceAllocator() + GpuMemoryHeap::~GpuMemoryHeap() { mDeferredReleases = decltype(mDeferredReleases)(); } - ResourceAllocator::SharedPtr ResourceAllocator::create(size_t pageSize, GpuFence::SharedPtr pFence) + GpuMemoryHeap::SharedPtr GpuMemoryHeap::create(Type type, size_t pageSize, const GpuFence::SharedPtr& pFence) { - SharedPtr pAllocator = SharedPtr(new ResourceAllocator(pageSize, pFence)); + SharedPtr pAllocator = SharedPtr(new GpuMemoryHeap(type, pageSize, pFence)); pAllocator->allocateNewPage(); return pAllocator; } - void ResourceAllocator::allocateNewPage() + void GpuMemoryHeap::allocateNewPage() { if (mpActivePage) { @@ -67,12 +67,12 @@ namespace Falcor mCurrentPageId++; } - ResourceAllocator::AllocationData ResourceAllocator::allocate(size_t size, size_t alignment) + GpuMemoryHeap::Allocation GpuMemoryHeap::allocate(size_t size, size_t alignment) { - AllocationData data; + Allocation data; if (size > mPageSize) { - data.pageID = ResourceAllocator::AllocationData::kMegaPageId; + data.pageID = GpuMemoryHeap::Allocation::kMegaPageId; initBasePageData(data, size); } else @@ -97,18 +97,18 @@ namespace Falcor return data; } - void ResourceAllocator::release(AllocationData& data) + void GpuMemoryHeap::release(Allocation& data) { assert(data.pResourceHandle); mDeferredReleases.push(data); } - void ResourceAllocator::executeDeferredReleases() + void GpuMemoryHeap::executeDeferredReleases() { uint64_t gpuVal = mpFence->getGpuValue(); while (mDeferredReleases.size() && mDeferredReleases.top().fenceValue <= gpuVal) { - const AllocationData& data = mDeferredReleases.top(); + const Allocation& data = mDeferredReleases.top(); if (data.pageID == mCurrentPageId) { mpActivePage->allocationsCount--; @@ -119,7 +119,7 @@ namespace Falcor } else { - if (data.pageID != AllocationData::kMegaPageId) + if (data.pageID != Allocation::kMegaPageId) { auto& pData = mUsedPages[data.pageID]; pData->allocationsCount--; diff --git a/Framework/Source/API/LowLevel/ResourceAllocator.h b/Source/Falcor/Core/API/GpuMemoryHeap.h similarity index 72% rename from Framework/Source/API/LowLevel/ResourceAllocator.h rename to Source/Falcor/Core/API/GpuMemoryHeap.h index 1143cea3c..308dc2985 100644 --- a/Framework/Source/API/LowLevel/ResourceAllocator.h +++ b/Source/Falcor/Core/API/GpuMemoryHeap.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,19 +26,25 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include #include -#include "GpuFence.h" +#include "Core/API/GpuFence.h" namespace Falcor -{ - class ResourceAllocator +{ + class dlldecl GpuMemoryHeap { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; - static SharedPtr create(size_t pageSize, GpuFence::SharedPtr pFence); + enum class Type + { + Default, + Upload, + Readback + }; + + static SharedPtr create(Type type, size_t pageSize, const GpuFence::SharedPtr& pFence); struct BaseData { ResourceHandle pResourceHandle; @@ -46,23 +52,23 @@ namespace Falcor uint8_t* pData = nullptr; }; - struct AllocationData : public BaseData + struct Allocation : public BaseData { uint64_t pageID = 0; uint64_t fenceValue = 0; static const uint64_t kMegaPageId = -1; - bool operator<(const AllocationData& other) const { return fenceValue > other.fenceValue; } + bool operator<(const Allocation& other) const { return fenceValue > other.fenceValue; } }; - ~ResourceAllocator(); + ~GpuMemoryHeap(); - AllocationData allocate(size_t size, size_t alignment = 1); - void release(AllocationData& data); + Allocation allocate(size_t size, size_t alignment = 1); + void release(Allocation& data); size_t getPageSize() const { return mPageSize; } void executeDeferredReleases(); private: - ResourceAllocator(size_t pageSize, GpuFence::SharedPtr pFence) : mPageSize(pageSize), mpFence(pFence) {} + GpuMemoryHeap(Type type, size_t pageSize, const GpuFence::SharedPtr& pFence) : mType(type), mPageSize(pageSize), mpFence(pFence) {} struct PageData : public BaseData { uint32_t allocationsCount = 0; @@ -70,17 +76,18 @@ namespace Falcor using UniquePtr = std::unique_ptr; }; - + + Type mType; GpuFence::SharedPtr mpFence; size_t mPageSize = 0; size_t mCurrentPageId = 0; PageData::UniquePtr mpActivePage; - std::priority_queue mDeferredReleases; + std::priority_queue mDeferredReleases; std::unordered_map mUsedPages; std::queue mAvailablePages; void allocateNewPage(); - static void initBasePageData(BaseData& data, size_t size); + void initBasePageData(BaseData& data, size_t size); }; } diff --git a/Framework/Source/API/GpuTimer.cpp b/Source/Falcor/Core/API/GpuTimer.cpp similarity index 94% rename from Framework/Source/API/GpuTimer.cpp rename to Source/Falcor/Core/API/GpuTimer.cpp index 41feab8fd..40f632eb2 100644 --- a/Framework/Source/API/GpuTimer.cpp +++ b/Source/Falcor/Core/API/GpuTimer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Device.h" +#include "stdafx.h" #include "GpuTimer.h" +#include "Buffer.h" +#include "Device.h" +#include "QueryHeap.h" +#include "RenderContext.h" namespace Falcor { @@ -105,4 +108,9 @@ namespace Falcor assert(mStatus == Status::Idle); return mElapsedTime; } + + SCRIPT_BINDING(GpuTimer) + { + m.regClass(GpuTimer); + } } diff --git a/Framework/Source/API/GpuTimer.h b/Source/Falcor/Core/API/GpuTimer.h similarity index 93% rename from Framework/Source/API/GpuTimer.h rename to Source/Falcor/Core/API/GpuTimer.h index e4ee9c9e4..d726fa976 100644 --- a/Framework/Source/API/GpuTimer.h +++ b/Source/Falcor/Core/API/GpuTimer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,17 +26,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/QueryHeap.h" -#include "API/Buffer.h" -#include "API/LowLevel/LowLevelContextData.h" -#include +#include "Core/API/LowLevelContextData.h" +#include "Core/API/Buffer.h" namespace Falcor -{ +{ /** Abstracts GPU timer queries. \n This class provides mechanism to get elapsed time in miliseconds between a pair of Begin()/End() calls. */ - class GpuTimer : public std::enable_shared_from_this + class dlldecl GpuTimer : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -87,4 +85,4 @@ namespace Falcor Buffer::SharedPtr mpResolveBuffer; // Yes, I know it's against my policy to put API specific code in common headers, but it's not worth the complications #endif }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/GraphicsStateObject.cpp b/Source/Falcor/Core/API/GraphicsStateObject.cpp similarity index 94% rename from Framework/Source/API/GraphicsStateObject.cpp rename to Source/Falcor/Core/API/GraphicsStateObject.cpp index 270045608..0671ac9f2 100644 --- a/Framework/Source/API/GraphicsStateObject.cpp +++ b/Source/Falcor/Core/API/GraphicsStateObject.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "GraphicsStateObject.h" -#include "BlendState.h" -#include "VAO.h" -#include "Device.h" +#include "Core/API/Device.h" namespace Falcor { @@ -100,10 +98,7 @@ namespace Falcor if (!pState->mDesc.mpRasterizerState) pState->mDesc.mpRasterizerState = spDefaultRasterizerState; if (!pState->mDesc.mpDepthStencilState) pState->mDesc.mpDepthStencilState = spDefaultDepthStencilState; - if (pState->apiInit() == false) - { - pState = nullptr; - } + if (pState->apiInit() == false) pState = nullptr; return pState; } } diff --git a/Framework/Source/API/GraphicsStateObject.h b/Source/Falcor/Core/API/GraphicsStateObject.h similarity index 91% rename from Framework/Source/API/GraphicsStateObject.h rename to Source/Falcor/Core/API/GraphicsStateObject.h index 76eff4e36..d510991bb 100644 --- a/Framework/Source/API/GraphicsStateObject.h +++ b/Source/Falcor/Core/API/GraphicsStateObject.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,21 +26,18 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/VertexLayout.h" -#include "API/FBO.h" -#include "Graphics/Program/ProgramVersion.h" -#include "API/RasterizerState.h" -#include "API/DepthStencilState.h" -#include "API/BlendState.h" -#include "API/LowLevel/RootSignature.h" -#include "API/VAO.h" +#include "Core/API/VertexLayout.h" +#include "Core/API/FBO.h" +#include "Core/Program/ProgramVersion.h" +#include "Core/API/RasterizerState.h" +#include "Core/API/DepthStencilState.h" +#include "Core/API/BlendState.h" +#include "Core/API/RootSignature.h" +#include "Core/API/VAO.h" namespace Falcor { - class RenderContext; - class GraphicsState; - - class GraphicsStateObject + class dlldecl GraphicsStateObject { public: using SharedPtr = std::shared_ptr; @@ -62,7 +59,7 @@ namespace Falcor Patch, }; - class Desc + class dlldecl Desc { public: Desc& setRootSignature(RootSignature::SharedPtr pSignature) { mpRootSignature = pSignature; return *this; } @@ -79,21 +76,21 @@ namespace Falcor BlendState::SharedPtr getBlendState() const { return mpBlendState; } RasterizerState::SharedPtr getRasterizerState() const { return mpRasterizerState; } DepthStencilState::SharedPtr getDepthStencilState() const { return mpDepthStencilState; } + ProgramVersion::SharedConstPtr getProgramVersion() const { return mpProgram; } + RootSignature::SharedPtr getRootSignature() const { return mpRootSignature; } uint32_t getSampleMask() const { return mSampleMask; } - GraphicsStateObject::PrimitiveType getPrimitiveType() const { return mPrimType; } VertexLayout::SharedConstPtr getVertexLayout() const { return mpLayout; } + PrimitiveType getPrimitiveType() const { return mPrimType; } Fbo::Desc getFboDesc() const { return mFboDesc; } - ProgramVersion::SharedConstPtr getProgramVersion() const { return mpProgram; } - RootSignature::SharedPtr getRootSignature() const { return mpRootSignature; } - + bool getSinglePassStereoEnabled() const { return mSinglePassStereoEnabled; } bool operator==(const Desc& other) const; private: friend class GraphicsStateObject; - VertexLayout::SharedConstPtr mpLayout; Fbo::Desc mFboDesc; + VertexLayout::SharedConstPtr mpLayout; ProgramVersion::SharedConstPtr mpProgram; RasterizerState::SharedPtr mpRasterizerState; DepthStencilState::SharedPtr mpDepthStencilState; @@ -133,4 +130,4 @@ namespace Falcor bool apiInit(); }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/LowLevel/LowLevelContextData.h b/Source/Falcor/Core/API/LowLevelContextData.h similarity index 93% rename from Framework/Source/API/LowLevel/LowLevelContextData.h rename to Source/Falcor/Core/API/LowLevelContextData.h index b91b87327..bda369401 100644 --- a/Framework/Source/API/LowLevel/LowLevelContextData.h +++ b/Source/Falcor/Core/API/LowLevelContextData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,13 +26,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/LowLevel/FencedPool.h" +#include "GpuFence.h" namespace Falcor { struct LowLevelContextApiData; - class LowLevelContextData : public std::enable_shared_from_this + class dlldecl LowLevelContextData : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -71,4 +71,4 @@ namespace Falcor CommandAllocatorHandle mpAllocator; GpuFence::SharedPtr mpFence; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/QueryHeap.h b/Source/Falcor/Core/API/QueryHeap.h similarity index 95% rename from Framework/Source/API/QueryHeap.h rename to Source/Falcor/Core/API/QueryHeap.h index 3b290746e..63b1988cc 100644 --- a/Framework/Source/API/QueryHeap.h +++ b/Source/Falcor/Core/API/QueryHeap.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ namespace Falcor { - class QueryHeap : public std::enable_shared_from_this + class dlldecl QueryHeap : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; diff --git a/Framework/Source/Utils/PatternGenerators/PatternGenerator.h b/Source/Falcor/Core/API/RasterizerState.cpp similarity index 80% rename from Framework/Source/Utils/PatternGenerators/PatternGenerator.h rename to Source/Falcor/Core/API/RasterizerState.cpp index fbe013d71..9b2a9c0bc 100644 --- a/Framework/Source/Utils/PatternGenerators/PatternGenerator.h +++ b/Source/Falcor/Core/API/RasterizerState.cpp @@ -25,20 +25,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#pragma once +#include "stdafx.h" +#include "RasterizerState.h" namespace Falcor { - class PatternGenerator : public std::enable_shared_from_this + SCRIPT_BINDING(RasterizerState) { - public: - using SharedPtr = std::shared_ptr; - virtual ~PatternGenerator() = default; - - virtual uint32_t getSampleCount() const = 0; - virtual void reset(uint32_t startID = 0) = 0; - virtual vec2 next() = 0; - protected: - PatternGenerator() = default; - }; -} \ No newline at end of file + m.regClass(RasterizerState) + auto rasterizerStateBinding = m.enum_("CullMode"); + rasterizerStateBinding.regEnumVal(RasterizerState::CullMode::Back).regEnumVal(RasterizerState::CullMode::Front).regEnumVal(RasterizerState::CullMode::None); + } +} diff --git a/Framework/Source/API/RasterizerState.h b/Source/Falcor/Core/API/RasterizerState.h similarity index 91% rename from Framework/Source/API/RasterizerState.h rename to Source/Falcor/Core/API/RasterizerState.h index ee62ed1e5..3951e5fea 100644 --- a/Framework/Source/API/RasterizerState.h +++ b/Source/Falcor/Core/API/RasterizerState.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ namespace Falcor { /** Rasterizer state */ - class RasterizerState : public std::enable_shared_from_this + class dlldecl RasterizerState : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -56,7 +56,7 @@ namespace Falcor /** Rasterizer state descriptor */ - class Desc + class dlldecl Desc { public: friend class RasterizerState; @@ -163,4 +163,20 @@ namespace Falcor RasterizerState(const Desc& Desc) : mDesc(Desc) {} Desc mDesc; }; -} \ No newline at end of file + + // FIXME: Added "Cull" prefix to the enum values as we can't register "None" +#define rasterizer_state_cm(a) case RasterizerState::CullMode::a: return "Cull" #a + inline std::string to_string(RasterizerState::CullMode st) + { + switch (st) + { + rasterizer_state_cm(None); + rasterizer_state_cm(Front); + rasterizer_state_cm(Back); + default: + should_not_get_here(); + return ""; + } + } +#undef rasterizer_state_cm +} diff --git a/Framework/Source/API/RenderContext.cpp b/Source/Falcor/Core/API/RenderContext.cpp similarity index 64% rename from Framework/Source/API/RenderContext.cpp rename to Source/Falcor/Core/API/RenderContext.cpp index 6c62c0ecd..b13f49304 100644 --- a/Framework/Source/API/RenderContext.cpp +++ b/Source/Falcor/Core/API/RenderContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,60 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RenderContext.h" -#include "RasterizerState.h" -#include "DepthStencilState.h" -#include "BlendState.h" #include "FBO.h" -#include "Device.h" +#include "Texture.h" namespace Falcor { - RenderContext::RenderContext() - { - if (gpDrawCommandSig == nullptr) - { - initDrawCommandSignatures(); - } - } - - void RenderContext::pushGraphicsState(const GraphicsState::SharedPtr& pState) - { - mPipelineStateStack.push(mpGraphicsState); - setGraphicsState(pState); - } - - void RenderContext::popGraphicsState() - { - if (mPipelineStateStack.empty()) - { - logWarning("Can't pop from the PipelineState stack. The stack is empty"); - return; - } - - setGraphicsState(mPipelineStateStack.top()); - mPipelineStateStack.pop(); - } - - void RenderContext::pushGraphicsVars(const GraphicsVars::SharedPtr& pVars) - { - mpGraphicsVarsStack.push(mpGraphicsVars); - setGraphicsVars(pVars); - } - - void RenderContext::popGraphicsVars() - { - if (mpGraphicsVarsStack.empty()) - { - logWarning("Can't pop from the graphics vars stack. The stack is empty"); - return; - } - - setGraphicsVars(mpGraphicsVarsStack.top()); - mpGraphicsVarsStack.pop(); - } - void RenderContext::clearFbo(const Fbo* pFbo, const glm::vec4& color, float depth, uint8_t stencil, FboAttachmentType flags) { bool hasDepthStencilTexture = pFbo->getDepthStencilTexture() != nullptr; @@ -105,24 +58,58 @@ namespace Falcor } } - void RenderContext::applyGraphicsVars() + + void RenderContext::clearTexture(Texture* pTexture, const vec4& clearColor) + { + assert(pTexture); + + // Check that the format is either Unorm, Snorm or float + auto format = pTexture->getFormat(); + auto fType = getFormatType(format); + if (fType == FormatType::Sint || fType == FormatType::Uint || fType == FormatType::Unknown) + { + logWarning("RenderContext::clearTexture() - Unsupported texture format " + to_string(format) + ". The texture format must be a normalized or floating-point format"); + return; + } + + auto bindFlags = pTexture->getBindFlags(); + // Select the right clear based on the texture's binding flags + if (is_set(bindFlags, Resource::BindFlags::RenderTarget)) clearRtv(pTexture->getRTV().get(), clearColor); + else if (is_set(bindFlags, Resource::BindFlags::UnorderedAccess)) clearUAV(pTexture->getUAV().get(), clearColor); + else if (is_set(bindFlags, Resource::BindFlags::DepthStencil)) + { + if (isStencilFormat(format) && (clearColor.y != 0)) + { + logWarning("RenderContext::clearTexture() - when clearing a depth-stencil texture the stencil value(clearColor.y) must be 0. Received " + std::to_string(clearColor.y) + ". Forcing stencil to 0"); + } + clearDsv(pTexture->getDSV().get(), clearColor.r, 0); + } + else + { + logWarning("Texture::clear() - The texture does not have a bind flag that allows us to clear!"); + } + } + + bool RenderContext::applyGraphicsVars(GraphicsVars* pVars) { - if (mpGraphicsVars->apply(this, mBindGraphicsRootSig) == false) + bool bindRootSig = (pVars != mpLastBoundGraphicsVars); + if (pVars->apply(this, bindRootSig) == false) { logWarning("RenderContext::prepareForDraw() - applying GraphicsVars failed, most likely because we ran out of descriptors. Flushing the GPU and retrying"); flush(true); - if (!mpGraphicsVars->apply(this, mBindGraphicsRootSig)) + if (!pVars->apply(this, bindRootSig)) { - logError("RenderContext::applyGraphicsVars() - applying GraphicsVars failed, most likely because we ran out of descriptors", true); - assert(false); + logError("RenderContext::applyGraphicsVars() - applying GraphicsVars failed, most likely because we ran out of descriptors"); + return false; } } + return true; } void RenderContext::flush(bool wait) { ComputeContext::flush(wait); - mBindGraphicsRootSig = true; + mpLastBoundGraphicsVars = nullptr; } } diff --git a/Framework/Source/API/RenderContext.h b/Source/Falcor/Core/API/RenderContext.h similarity index 68% rename from Framework/Source/API/RenderContext.h rename to Source/Falcor/Core/API/RenderContext.h index cc6a955d6..c93c11c40 100644 --- a/Framework/Source/API/RenderContext.h +++ b/Source/Falcor/Core/API/RenderContext.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,17 +28,9 @@ #pragma once #include #include -#include "API/Sampler.h" -#include "API/FBO.h" -#include "API/VAO.h" -#include "API/StructuredBuffer.h" -#include "API/Texture.h" -#include "Framework.h" -#include "API/GraphicsStateObject.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/GraphicsState.h" -#include "API/ComputeContext.h" -#include "Graphics/FullScreenPass.h" +#include "ComputeContext.h" +#include "Sampler.h" +#include "Core/State/GraphicsState.h" namespace Falcor { @@ -47,9 +39,11 @@ namespace Falcor class RtState; #endif + class FullScreenPass; + /** The rendering context. Use it to bind state and dispatch calls to the GPU */ - class RenderContext : public ComputeContext + class dlldecl RenderContext : public ComputeContext { public: using SharedPtr = std::shared_ptr; @@ -104,11 +98,18 @@ namespace Falcor */ void clearDsv(const DepthStencilView* pDsv, float depth, uint8_t stencil, bool clearDepth = true, bool clearStencil = true); + /** Clear a texture. The function will use the bind-flags to find the optimal API call to make + \param[in] pTexture The texture to clear + \param[in] clearColor The clear color + The function only support floating-point and normalized color-formats and depth. For depth buffers, `clearColor.x` will be used. If there's a stencil-channel, `clearColor.y` must be zero + */ + void clearTexture(Texture* pTexture, const vec4& clearColor = vec4(0, 0, 0, 1)); + /** Ordered draw call. \param[in] vertexCount Number of vertices to draw \param[in] startVertexLocation The location of the first vertex to read from the vertex buffers (offset in vertices) */ - void draw(uint32_t vertexCount, uint32_t startVertexLocation); + void draw(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation); /** Ordered instanced draw call. \param[in] vertexCount Number of vertices to draw @@ -116,14 +117,14 @@ namespace Falcor \param[in] startVertexLocation The location of the first vertex to read from the vertex buffers (offset in vertices) \param[in] startInstanceLocation A value which is added to each index before reading per-instance data from the vertex buffer */ - void drawInstanced(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation); + void drawInstanced(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation); /** Indexed draw call. \param[in] indexCount Number of indices to draw \param[in] startIndexLocation The location of the first index to read from the index buffer (offset in indices) \param[in] baseVertexLocation A value which is added to each index before reading a vertex from the vertex buffer */ - void drawIndexed(uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation); + void drawIndexed(GraphicsState* pState, GraphicsVars* pVars, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation); /** Indexed instanced draw call. \param[in] indexCount Number of indices to draw per instance @@ -132,19 +133,25 @@ namespace Falcor \param[in] baseVertexLocation A value which is added to each index before reading a vertex from the vertex buffer \param[in] startInstanceLocation A value which is added to each index before reading per-instance data from the vertex buffer */ - void drawIndexedInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation); + void drawIndexedInstanced(GraphicsState* pState, GraphicsVars* pVars, uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation); /** Executes an indirect draw call. + \param[in] maxCommandCount If pCountBuffer is null, this specifies the command count. Otherwise, command count is minimum of maxCommandCount and the value contained in pCountBuffer \param[in] pArgBuffer Buffer containing draw arguments \param[in] argBufferOffset Offset into buffer to read arguments from + \param[in] pCountBuffer Optional. A GPU buffer that contains a uint32 value specifying the command count. This can, but does not have to be a dedicated buffer + \param[in] countBufferOffset Offset into pCountBuffer to read the value from */ - void drawIndirect(const Buffer* pArgBuffer, uint64_t argBufferOffset); + void drawIndirect(GraphicsState* pState, GraphicsVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, const Buffer* pCountBuffer, uint64_t countBufferOffset); /** Executes an indirect draw-indexed call. + \param[in] maxCommandCount If pCountBuffer is null, this specifies the command count. Otherwise, command count is minimum of maxCommandCount and the value contained in pCountBuffer \param[in] pArgBuffer Buffer containing draw arguments \param[in] argBufferOffset Offset into buffer to read arguments from + \param[in] pCountBuffer Optional. A GPU buffer that contains a uint32 value specifying the command count. This can, but does not have to be a dedicated buffer + \param[in] countBufferOffset Offset into pCountBuffer to read the value from */ - void drawIndexedIndirect(const Buffer* pArgBuffer, uint64_t argBufferOffset); + void drawIndexedIndirect(GraphicsState* pState, GraphicsVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, const Buffer* pCountBuffer, uint64_t countBufferOffset); /** Blits (low-level copy) an SRV into an RTV. \param[in] pSrc Source view to copy from @@ -154,38 +161,6 @@ namespace Falcor */ void blit(ShaderResourceView::SharedPtr pSrc, RenderTargetView::SharedPtr pDst, const uvec4& srcRect = uvec4(-1), const uvec4& dstRect = uvec4(-1), Sampler::Filter = Sampler::Filter::Linear); - /** Set the program variables for graphics - */ - void setGraphicsVars(const GraphicsVars::SharedPtr& pVars) { mBindGraphicsRootSig = true;/* mBindGraphicsRootSig || (mpGraphicsVars != pVars)*/; mpGraphicsVars = pVars; } - - /** Get the bound graphics program variables object - */ - const GraphicsVars::SharedPtr& getGraphicsVars() const { return mpGraphicsVars; } - - /** Push the current graphics vars and sets a new one - */ - void pushGraphicsVars(const GraphicsVars::SharedPtr& pVars); - - /** Pops the last ProgramVars from the stack and sets it - */ - void popGraphicsVars(); - - /** Set a graphics state - */ - void setGraphicsState(const GraphicsState::SharedPtr& pState) { mpGraphicsState = pState; } - - /** Get the currently bound graphics state - */ - GraphicsState::SharedPtr getGraphicsState() const { return mpGraphicsState; } - - /** Push the current graphics state and sets a new one - */ - void pushGraphicsState(const GraphicsState::SharedPtr& pState); - - /** Pops the last graphics state from the stack and sets it - */ - void popGraphicsState(); - /** Submit the command list */ void flush(bool wait = false) override; @@ -208,59 +183,20 @@ namespace Falcor void resolveSubresource(const Texture::SharedPtr& pSrc, uint32_t srcSubresource, const Texture::SharedPtr& pDst, uint32_t dstSubresource); #ifdef FALCOR_D3D12 - /** Submit a raytrace command. This function doesn't change the state of the render-context. Graphics/compute vars and state will stay the same + /** Submit a raytrace command. This function doesn't change the state of the render-context. Graphics/compute vars and state will stay the same. */ - deprecate("3.3", "Ray dispatch now accepts depth as a parameter. Using the deprecated version will assume depth = 1.") - void raytrace(std::shared_ptr pVars, std::shared_ptr pState, uint32_t width, uint32_t height); void raytrace(std::shared_ptr pVars, std::shared_ptr pState, uint32_t width, uint32_t height, uint32_t depth); #endif private: RenderContext(); - GraphicsVars::SharedPtr mpGraphicsVars; - GraphicsState::SharedPtr mpGraphicsState; - bool mBindGraphicsRootSig = true; - - std::stack mPipelineStateStack; - std::stack mpGraphicsVarsStack; - - /** Creates command signatures for DrawIndirect, DrawIndexedIndirect. Also calls - compute context's initDispatchCommandSignature() to create command signature for dispatchIndirect - */ - static void initDrawCommandSignatures(); - void applyGraphicsVars(); + GraphicsVars* mpLastBoundGraphicsVars; + bool applyGraphicsVars(GraphicsVars* pVars); // Internal functions used by the API layers - void prepareForDraw(); + bool prepareForDraw(GraphicsState* pState, GraphicsVars* pVars); StateBindFlags mBindFlags = StateBindFlags::All; }; enum_class_operators(RenderContext::StateBindFlags); - -#ifndef FALCOR_VK - struct BlitData - { - FullScreenPass::UniquePtr pPass; - GraphicsVars::SharedPtr pVars; - GraphicsState::SharedPtr pState; - - Sampler::SharedPtr pLinearSampler; - Sampler::SharedPtr pPointSampler; - - ConstantBuffer::SharedPtr pSrcRectBuffer; - vec2 prevSrcRectOffset = vec2(0,0); - vec2 prevSrcReftScale = vec2(0,0); - - // Variable offsets in constant buffer - size_t offsetVarOffset = ConstantBuffer::kInvalidOffset; - size_t scaleVarOffset = ConstantBuffer::kInvalidOffset;; - - ProgramReflection::BindLocation texBindLoc; - ProgramReflection::BindLocation samplerBindLoc; - }; - - dlldecl BlitData gBlitData; -#endif - dlldecl CommandSignatureHandle gpDrawCommandSig; - dlldecl CommandSignatureHandle gpDrawIndexCommandSig; } diff --git a/Framework/Source/API/Resource.cpp b/Source/Falcor/Core/API/Resource.cpp similarity index 50% rename from Framework/Source/API/Resource.cpp rename to Source/Falcor/Core/API/Resource.cpp index def2f20af..fd387e137 100644 --- a/Framework/Source/API/Resource.cpp +++ b/Source/Falcor/Core/API/Resource.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Resource.h" #include "Texture.h" @@ -51,32 +51,6 @@ namespace Falcor #undef type_2_string } - const std::string to_string(Resource::BindFlags flags) - { - std::string s; - if (flags == Resource::BindFlags::None) - { - return "None"; - } - -#define flag_to_str(f_) if (is_set(flags, Resource::BindFlags::f_)) (s += (s.size() ? " | " : "") + std::string(#f_)) - - flag_to_str(Vertex); - flag_to_str(Index); - flag_to_str(Constant); - flag_to_str(StreamOutput); - flag_to_str(ShaderResource); - flag_to_str(UnorderedAccess); - flag_to_str(RenderTarget); - flag_to_str(DepthStencil); -#ifdef FALCOR_D3D12 - flag_to_str(AccelerationStructure); -#endif -#undef flag_to_str - - return s; - } - const std::string to_string(Resource::State state) { if (state == Resource::State::Common) @@ -110,109 +84,6 @@ namespace Falcor return s; } - template - using CreateFuncType = std::function; - - template - typename ViewClass::SharedPtr findViewCommon(Resource* pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize, ViewMapType& viewMap, CreateFuncType createFunc) - { - uint32_t resMipCount = 1; - uint32_t resArraySize = 1; - - const Texture* pTexture = dynamic_cast(pResource); - - if (pTexture) - { - resArraySize = pTexture->getArraySize(); - resMipCount = pTexture->getMipCount(); - } - else - { - assert(pResource->getType() == Resource::Type::Buffer); - } - - if (firstArraySlice >= resArraySize) - { - logWarning("First array slice is OOB when creating resource view. Clamping"); - firstArraySlice = resArraySize - 1; - } - - if (mostDetailedMip >= resMipCount) - { - logWarning("Most detailed mip is OOB when creating resource view. Clamping"); - mostDetailedMip = resMipCount - 1; - } - - if (mipCount == Resource::kMaxPossible) - { - mipCount = resMipCount - mostDetailedMip; - } - else if (mipCount + mostDetailedMip > resMipCount) - { - logWarning("Mip count is OOB when creating resource view. Clamping"); - mipCount = resMipCount - mostDetailedMip; - } - - if (arraySize == Resource::kMaxPossible) - { - arraySize = resArraySize - firstArraySlice; - } - else if (arraySize + firstArraySlice > resArraySize) - { - logWarning("Array size is OOB when creating resource view. Clamping"); - arraySize = resArraySize - firstArraySlice; - } - - ResourceViewInfo view = ResourceViewInfo(mostDetailedMip, mipCount, firstArraySlice, arraySize); - - if (viewMap.find(view) == viewMap.end()) - { - viewMap[view] = createFunc(pResource, mostDetailedMip, mipCount, firstArraySlice, arraySize); - } - - return viewMap[view]; - } - - DepthStencilView::SharedPtr Resource::getDSV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - auto createFunc = [](Resource* pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) - { - return DepthStencilView::create(pResource->shared_from_this(), mostDetailedMip, firstArraySlice, arraySize); - }; - - return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mDsvs, createFunc); - } - - UnorderedAccessView::SharedPtr Resource::getUAV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - auto createFunc = [](Resource* pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) - { - return UnorderedAccessView::create(pResource->shared_from_this(), mostDetailedMip, firstArraySlice, arraySize); - }; - - return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mUavs, createFunc); - } - - RenderTargetView::SharedPtr Resource::getRTV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) - { - auto createFunc = [](Resource* pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) - { - return RenderTargetView::create(pResource->shared_from_this(), mostDetailedMip, firstArraySlice, arraySize); - }; - - return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mRtvs, createFunc); - } - - ShaderResourceView::SharedPtr Resource::getSRV(uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) - { - auto createFunc = [](Resource* pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) - { - return ShaderResourceView::create(pResource->shared_from_this(), mostDetailedMip, mipCount, firstArraySlice, arraySize); - }; - - return findViewCommon(this, mostDetailedMip, mipCount, firstArraySlice, arraySize, mSrvs, createFunc); - } - void Resource::invalidateViews() const { logInfo("Invalidating resource views"); @@ -271,4 +142,9 @@ namespace Falcor mState.isGlobal = false; mState.perSubresource[pTexture->getSubresourceIndex(arraySlice, mipLevel)] = newState; } -} \ No newline at end of file + + SCRIPT_BINDING(Resource) + { + auto c = m.regClass(Resource); + } +} diff --git a/Framework/Source/API/Resource.h b/Source/Falcor/Core/API/Resource.h similarity index 69% rename from Framework/Source/API/Resource.h rename to Source/Falcor/Core/API/Resource.h index 5c885eddf..73099c04f 100644 --- a/Framework/Source/API/Resource.h +++ b/Source/Falcor/Core/API/Resource.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,9 +31,14 @@ namespace Falcor { - class CopyContext; - - class Resource : public std::enable_shared_from_this + class Texture; + class Buffer; + class StructuredBuffer; + class TypedBufferBase; + template class TypedBuffer; + class ConstantBuffer; + + class dlldecl Resource : public std::enable_shared_from_this { public: using ApiHandle = ResourceHandle; @@ -111,46 +116,26 @@ namespace Falcor */ const ApiHandle& getApiHandle() const { return mApiHandle; } - /** Get a shader-resource view. - \param[in] mostDetailedMip The most detailed mip level of the view - \param[in] mipCount The number of mip-levels to bind. If this is equal to Texture#kMaxPossible, will create a view ranging from mostDetailedMip to the texture's mip levels count - \param[in] firstArraySlice The first array slice of the view - \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size - */ - ShaderResourceView::SharedPtr getSRV(uint32_t mostDetailedMip = 0, uint32_t mipCount = kMaxPossible, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); - - /** Get a render-target view. - \param[in] mipLevel The requested mip-level - \param[in] firstArraySlice The first array slice of the view - \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size - */ - RenderTargetView::SharedPtr getRTV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); - - /** Get a depth stencil view. - \param[in] mipLevel The requested mip-level - \param[in] firstArraySlice The first array slice of the view - \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size - */ - DepthStencilView::SharedPtr getDSV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); - - /** Get an unordered access view. - \param[in] mipLevel The requested mip-level - \param[in] firstArraySlice The first array slice of the view - \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size + /** Creates a shared resource API handle. */ - UnorderedAccessView::SharedPtr getUAV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + SharedResourceApiHandle createSharedApiHandle(); struct ViewInfoHashFunc { std::size_t operator()(const ResourceViewInfo& v) const { - return ((std::hash()(v.firstArraySlice) - ^ (std::hash()(v.arraySize) << 1)) >> 1) + return ((std::hash()(v.firstArraySlice) ^ (std::hash()(v.arraySize) << 1)) >> 1) ^ (std::hash()(v.mipCount) << 1) - ^ (std::hash()(v.mostDetailedMip) << 3); + ^ (std::hash()(v.mostDetailedMip) << 3) + ^ (std::hash()(v.firstElement) << 5) + ^ (std::hash()(v.elementCount) << 7); } }; + /** Get the size of the resource + */ + size_t getSize() const { return mSize; } + /** Invalidate and release all of the resource views */ void invalidateViews() const; @@ -163,10 +148,20 @@ namespace Falcor */ const std::string& getName() const { return mName; } + /** Conversions to derived classes + */ + std::shared_ptr asTexture() { return this ? std::dynamic_pointer_cast(shared_from_this()) : nullptr; } + std::shared_ptr asBuffer() { return this ? std::dynamic_pointer_cast(shared_from_this()) : nullptr; } + std::shared_ptr asStructuredBuffer() { return this ? std::dynamic_pointer_cast(shared_from_this()) : nullptr; } + std::shared_ptr asTypedBufferBase() { return this ? std::dynamic_pointer_cast(shared_from_this()) : nullptr; } + template + std::shared_ptr> asTypedBuffer() { return this ? std::dynamic_pointer_cast>(shared_from_this()) :nullptr; } + std::shared_ptr asConstantBuffer() { return this ? std::dynamic_pointer_cast(shared_from_this()) : nullptr; } + protected: friend class CopyContext; - Resource(Type type, BindFlags bindFlags) : mType(type), mBindFlags(bindFlags) {} + Resource(Type type, BindFlags bindFlags, uint64_t size) : mType(type), mBindFlags(bindFlags), mSize(size) {} Type mType; BindFlags mBindFlags; @@ -182,6 +177,8 @@ namespace Falcor void apiSetName(); ApiHandle mApiHandle; + size_t mSize = 0; + GpuAddress mGpuVaOffset = 0; std::string mName; mutable std::unordered_map mSrvs; @@ -190,9 +187,6 @@ namespace Falcor mutable std::unordered_map mUavs; }; - enum_class_operators(Resource::BindFlags); - - const std::string to_string(Resource::Type); - const std::string to_string(Resource::BindFlags); - const std::string to_string(Resource::State); -} \ No newline at end of file + const std::string dlldecl to_string(Resource::Type); + const std::string dlldecl to_string(Resource::State); +} diff --git a/Source/Falcor/Core/API/ResourceViews.cpp b/Source/Falcor/Core/API/ResourceViews.cpp new file mode 100644 index 000000000..32411ad56 --- /dev/null +++ b/Source/Falcor/Core/API/ResourceViews.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ResourceViews.h" + +namespace Falcor +{ + static NullResourceViews gNullViews; + ResourceWeakPtr getEmptyTexture(); + + void createNullViews() + { + gNullViews.srv = ShaderResourceView::create(getEmptyTexture(), 0, 1, 0, 1); + gNullViews.dsv = DepthStencilView::create(getEmptyTexture(), 0, 0, 1); + gNullViews.uav = UnorderedAccessView::create(getEmptyTexture(), 0, 0, 1); + gNullViews.rtv = RenderTargetView::create(getEmptyTexture(), 0, 0, 1); + gNullViews.cbv = ConstantBufferView::create(ResourceWeakPtr()); + } + + void releaseNullViews() + { + gNullViews = {}; + } + + ShaderResourceView::SharedPtr ShaderResourceView::getNullView() { return gNullViews.srv; } + DepthStencilView::SharedPtr DepthStencilView::getNullView() { return gNullViews.dsv; } + UnorderedAccessView::SharedPtr UnorderedAccessView::getNullView() { return gNullViews.uav; } + RenderTargetView::SharedPtr RenderTargetView::getNullView() { return gNullViews.rtv;} + ConstantBufferView::SharedPtr ConstantBufferView::getNullView() { return gNullViews.cbv;} + + SCRIPT_BINDING(ResourceView) + { + m.regClass(ShaderResourceView); + m.regClass(RenderTargetView); + m.regClass(UnorderedAccessView); + m.regClass(ConstantBufferView); + m.regClass(DepthStencilView); + } +} diff --git a/Framework/Source/API/ResourceViews.h b/Source/Falcor/Core/API/ResourceViews.h similarity index 64% rename from Framework/Source/API/ResourceViews.h rename to Source/Falcor/Core/API/ResourceViews.h index af5de7d3b..28c1536a1 100644 --- a/Framework/Source/API/ResourceViews.h +++ b/Source/Falcor/Core/API/ResourceViews.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,26 +33,42 @@ namespace Falcor class Resource; using ResourceWeakPtr = std::weak_ptr; - struct ResourceViewInfo + struct dlldecl ResourceViewInfo { ResourceViewInfo() = default; - ResourceViewInfo(uint32_t mostDetailedMip_, uint32_t mipCount_, uint32_t firstArraySlice_, uint32_t arraySize_) : mostDetailedMip(mostDetailedMip_), mipCount(mipCount_), firstArraySlice(firstArraySlice_), arraySize(arraySize_) {} + ResourceViewInfo(uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + : mostDetailedMip(mostDetailedMip), mipCount(mipCount), firstArraySlice(firstArraySlice), arraySize(arraySize) {} + + ResourceViewInfo(uint32_t firstElement, uint32_t elementCount) + : firstElement(firstElement), elementCount(elementCount) {} + + static const uint32_t kMaxPossible = -1; + + // Textures uint32_t mostDetailedMip = 0; uint32_t mipCount = kMaxPossible; uint32_t firstArraySlice = 0; uint32_t arraySize = kMaxPossible; - static const uint32_t kMaxPossible = -1; + // Buffers + uint32_t firstElement = 0; + uint32_t elementCount = kMaxPossible; + bool operator==(const ResourceViewInfo& other) const { - return (firstArraySlice == other.firstArraySlice) && (arraySize == other.arraySize) && (mipCount == other.mipCount) && (mostDetailedMip == other.mostDetailedMip); + return (firstArraySlice == other.firstArraySlice) + && (arraySize == other.arraySize) + && (mipCount == other.mipCount) + && (mostDetailedMip == other.mostDetailedMip) + && (firstElement == other.firstElement) + && (elementCount == other.elementCount); } }; /** Abstracts API resource views. */ template - class ResourceView + class dlldecl ResourceView { public: using ApiHandle = ApiHandleType; @@ -62,6 +78,9 @@ namespace Falcor ResourceView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) : mApiHandle(handle), mpResource(pResource), mViewInfo(mostDetailedMip, mipCount, firstArraySlice, arraySize) {} + ResourceView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t firstElement, uint32_t elementCount) + : mApiHandle(handle), mpResource(pResource), mViewInfo(firstElement, elementCount) {} + /** Get the raw API handle. */ const ApiHandle& getApiHandle() const { return mApiHandle; } @@ -79,50 +98,61 @@ namespace Falcor ResourceWeakPtr mpResource; }; - class ShaderResourceView : public ResourceView + class dlldecl ShaderResourceView : public ResourceView { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - - static SharedPtr create(ResourceWeakPtr pResource, uint32_t mostDetailedMip = 0, uint32_t mipCount = kMaxPossible, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + + static SharedPtr create(ResourceWeakPtr pResource, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize); + static SharedPtr create(ResourceWeakPtr pResource, uint32_t firstElement, uint32_t elementCount); static SharedPtr getNullView(); - ShaderResourceView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t mostDetailedMip_, uint32_t mipCount_, uint32_t firstArraySlice_, uint32_t arraySize_) : - ResourceView(pResource, handle, mostDetailedMip_, mipCount_, firstArraySlice_, arraySize_) {} + + // This is currently used by RtScene to create an SRV for the TLAS, since the create() functions above assume texture or buffer types. + ShaderResourceView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + : ResourceView(pResource, handle, mostDetailedMip, mipCount, firstArraySlice, arraySize) {} private: + + ShaderResourceView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t firstElement, uint32_t elementCount) + : ResourceView(pResource, handle, firstElement, elementCount) {} }; - class DepthStencilView : public ResourceView + class dlldecl DepthStencilView : public ResourceView { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - static SharedPtr create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + static SharedPtr create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize); static SharedPtr getNullView(); private: DepthStencilView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) : ResourceView(pResource, handle, mipLevel, 1, firstArraySlice, arraySize) {} }; - class UnorderedAccessView : public ResourceView + class dlldecl UnorderedAccessView : public ResourceView { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - static SharedPtr create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + + static SharedPtr create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize); + static SharedPtr create(ResourceWeakPtr pResource, uint32_t firstElement, uint32_t elementCount); static SharedPtr getNullView(); private: UnorderedAccessView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) : ResourceView(pResource, handle, mipLevel, 1, firstArraySlice, arraySize) {} + + UnorderedAccessView(ResourceWeakPtr& pResource, ApiHandle handle, uint32_t firstElement, uint32_t elementCount) + : ResourceView(pResource, handle, firstElement, elementCount) {} }; - class RenderTargetView : public ResourceView + class dlldecl RenderTargetView : public ResourceView { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - static SharedPtr create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + static SharedPtr create(ResourceWeakPtr pResource, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize); static SharedPtr getNullView(); ~RenderTargetView(); private: @@ -130,7 +160,7 @@ namespace Falcor ResourceView(pResource, handle, mipLevel, 1, firstArraySlice, arraySize) {} }; - class ConstantBufferView : public ResourceView + class dlldecl ConstantBufferView : public ResourceView { public: using SharedPtr = std::shared_ptr; @@ -143,9 +173,12 @@ namespace Falcor ResourceView(pResource, handle, 0, 1, 0, 1) {} }; - dlldecl ShaderResourceView::SharedPtr gNullSrv; - dlldecl ConstantBufferView::SharedPtr gNullCbv; - dlldecl RenderTargetView::SharedPtr gNullRtv; - dlldecl UnorderedAccessView::SharedPtr gNullUav; - dlldecl DepthStencilView::SharedPtr gNullDsv; + struct NullResourceViews + { + ShaderResourceView::SharedPtr srv; + ConstantBufferView::SharedPtr cbv; + RenderTargetView::SharedPtr rtv; + UnorderedAccessView::SharedPtr uav; + DepthStencilView::SharedPtr dsv; + }; } diff --git a/Framework/Source/API/LowLevel/RootSignature.cpp b/Source/Falcor/Core/API/RootSignature.cpp similarity index 94% rename from Framework/Source/API/LowLevel/RootSignature.cpp rename to Source/Falcor/Core/API/RootSignature.cpp index 162b3b178..948027932 100644 --- a/Framework/Source/API/LowLevel/RootSignature.cpp +++ b/Source/Falcor/Core/API/RootSignature.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/RootSignature.h" -#include "Graphics/Program/ProgramReflection.h" +#include "stdafx.h" +#include "RootSignature.h" +#include "Core/Program/ProgramReflection.h" namespace Falcor { @@ -82,14 +82,16 @@ namespace Falcor switch (type) { case RootSignature::DescType::TextureSrv: + case RootSignature::DescType::RawBufferSrv: case RootSignature::DescType::TypedBufferSrv: case RootSignature::DescType::StructuredBufferSrv: case RootSignature::DescType::Cbv: case RootSignature::DescType::Sampler: return ReflectionResourceType::ShaderAccess::Read; case RootSignature::DescType::TextureUav: - case RootSignature::DescType::StructuredBufferUav: + case RootSignature::DescType::RawBufferUav: case RootSignature::DescType::TypedBufferUav: + case RootSignature::DescType::StructuredBufferUav: return ReflectionResourceType::ShaderAccess::ReadWrite; default: should_not_get_here(); @@ -126,4 +128,4 @@ namespace Falcor RootSignature::Desc d = getRootDescFromReflector(pReflector, isLocal); return RootSignature::create(d); } -} \ No newline at end of file +} diff --git a/Framework/Source/API/LowLevel/RootSignature.h b/Source/Falcor/Core/API/RootSignature.h similarity index 95% rename from Framework/Source/API/LowLevel/RootSignature.h rename to Source/Falcor/Core/API/RootSignature.h index 9877c6a6d..2df8cc1ca 100644 --- a/Framework/Source/API/LowLevel/RootSignature.h +++ b/Source/Falcor/Core/API/RootSignature.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,15 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/Sampler.h" -#include "API/DescriptorSet.h" +#include "DescriptorSet.h" namespace Falcor { class ProgramReflection; class CopyContext; - class RootSignature + class dlldecl RootSignature { public: using SharedPtr = std::shared_ptr; @@ -44,7 +43,7 @@ namespace Falcor using DescType = Falcor::DescriptorSet::Type; using DescriptorSetLayout = DescriptorSet::Layout; - class Desc + class dlldecl Desc { public: Desc& addDescriptorSet(const DescriptorSetLayout& setLayout); @@ -92,4 +91,4 @@ namespace Falcor uint32_t mSizeInBytes; std::vector mElementByteOffset; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/Sampler.cpp b/Source/Falcor/Core/API/Sampler.cpp similarity index 78% rename from Framework/Source/API/Sampler.cpp rename to Source/Falcor/Core/API/Sampler.cpp index 4f81d91b9..55f22af57 100644 --- a/Framework/Source/API/Sampler.cpp +++ b/Source/Falcor/Core/API/Sampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Sampler.h" +#include "stdafx.h" +#include "Sampler.h" namespace Falcor { + struct SamplerData + { + uint32_t objectCount = 0; + Sampler::SharedPtr pDefaultSampler; + }; + SamplerData gSamplerData; + Sampler::Sampler(const Desc& desc) : mDesc(desc) { gSamplerData.objectCount++; @@ -38,10 +45,7 @@ namespace Falcor Sampler::~Sampler() { gSamplerData.objectCount--; - if (gSamplerData.objectCount == 1 && gSamplerData.pDefaultSampler) - { - gSamplerData.pDefaultSampler = nullptr; - } + if (gSamplerData.objectCount <= 1) gSamplerData.pDefaultSampler = nullptr; } Sampler::Desc& Sampler::Desc::setFilterMode(Filter minFilter, Filter magFilter, Filter mipFilter) @@ -94,4 +98,16 @@ namespace Falcor } return gSamplerData.pDefaultSampler; } + + SCRIPT_BINDING(Sampler) + { + m.regClass(Sampler); + + auto filter = m.enum_("SamplerFilter"); + filter.regEnumVal(Sampler::Filter::Linear).regEnumVal(Sampler::Filter::Point); + + auto addressing = m.enum_("AddressMode"); + addressing.regEnumVal(Sampler::AddressMode::Wrap).regEnumVal(Sampler::AddressMode::Mirror).regEnumVal(Sampler::AddressMode::Clamp); + addressing.regEnumVal(Sampler::AddressMode::Border).regEnumVal(Sampler::AddressMode::MirrorOnce); + } } diff --git a/Framework/Source/API/Sampler.h b/Source/Falcor/Core/API/Sampler.h similarity index 94% rename from Framework/Source/API/Sampler.h rename to Source/Falcor/Core/API/Sampler.h index c346c075c..260aab1e6 100644 --- a/Framework/Source/API/Sampler.h +++ b/Source/Falcor/Core/API/Sampler.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,17 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec4.hpp" namespace Falcor { /** Abstract the API sampler state object */ - class Sampler : public std::enable_shared_from_this + class dlldecl Sampler : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; using ApiHandle = SamplerHandle; /** Filter mode @@ -64,7 +64,7 @@ namespace Falcor /** Descriptor used to create a new Sampler object */ - class Desc + class dlldecl Desc { public: friend class Sampler; @@ -103,9 +103,9 @@ namespace Falcor Desc& setBorderColor(const glm::vec4& borderColor); protected: - Filter mMagFilter = Filter::Point; - Filter mMinFilter = Filter::Point; - Filter mMipFilter = Filter::Point; + Filter mMagFilter = Filter::Linear; + Filter mMinFilter = Filter::Linear; + Filter mMipFilter = Filter::Linear; uint32_t mMaxAnisotropy = 1; float mMaxLod = 1000; float mMinLod = -1000; @@ -191,13 +191,6 @@ namespace Falcor static uint32_t getApiMaxAnisotropy(); }; - struct SamplerData - { - uint32_t objectCount = 0; - Sampler::SharedPtr pDefaultSampler; - }; - dlldecl SamplerData gSamplerData; - #define filter_str(a) case Sampler::Filter::a: return #a inline std::string to_string(Sampler::Filter f) { @@ -224,4 +217,4 @@ namespace Falcor } } #undef address_str -} \ No newline at end of file +} diff --git a/Framework/Source/API/Shader.h b/Source/Falcor/Core/API/Shader.h similarity index 92% rename from Framework/Source/API/Shader.h rename to Source/Falcor/Core/API/Shader.h index 69b246ec2..6c39f80e9 100644 --- a/Framework/Source/API/Shader.h +++ b/Source/Falcor/Core/API/Shader.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,17 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include +#include +#include -#include "Externals/Slang/slang.h" +struct ISlangBlob; namespace Falcor { /** Minimal smart pointer for working with COM objects. */ template - struct ComPtr + struct dlldecl ComPtr { public: /// Type of the smart pointer itself @@ -131,7 +131,7 @@ namespace Falcor /** Low-level shader object This class abstracts the API's shader creation and management */ - class Shader : public std::enable_shared_from_this + class dlldecl Shader : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -147,6 +147,7 @@ namespace Falcor DumpIntermediates = 0x2, FloatingPointModeFast = 0x4, FloatingPointModePrecise = 0x8, + GenerateDebugInfo = 0x10, }; class DefineList : public std::map @@ -164,6 +165,13 @@ namespace Falcor \return The updated list of macro definitions. */ DefineList& remove(const std::string& name) { (*this).erase(name); return *this; } + + /** Add a define list to the current list + */ + DefineList& add(const DefineList& dl) { for (const auto& p : dl) add(p.first, p.second); return *this; } + + DefineList() = default; + DefineList(std::initializer_list> il) : std::map(il) {} }; /** Create a shader object @@ -202,4 +210,4 @@ namespace Falcor void* mpPrivateData = nullptr; }; enum_class_operators(Shader::CompilerFlags); -} \ No newline at end of file +} diff --git a/Source/Falcor/Core/API/Texture.cpp b/Source/Falcor/Core/API/Texture.cpp new file mode 100644 index 000000000..657840af9 --- /dev/null +++ b/Source/Falcor/Core/API/Texture.cpp @@ -0,0 +1,352 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Texture.h" +#include "Device.h" +#include "RenderContext.h" +#include "Utils/Threading.h" + +namespace Falcor +{ + namespace + { + Texture::BindFlags updateBindFlags(Texture::BindFlags flags, bool hasInitData, uint32_t mipLevels, ResourceFormat format, const std::string& texType) + { + if ((mipLevels == Texture::kMaxPossible) && hasInitData) + { + flags |= Texture::BindFlags::RenderTarget; + } + + Texture::BindFlags supported = getFormatBindFlags(format); + supported |= ResourceBindFlags::Shared; + if ((flags & supported) != flags) + { + logError("Error when creating " + texType + " of format " + to_string(format) + ". The requested bind-flags are not supported.\n" + "Requested = (" + to_string(flags) + "), supported = (" + to_string(supported) + ").\n\n" + "The texture will be created only with the supported bind flags, which may result in a crash or a rendering error"); + flags = flags & supported; + } + + return flags; + } + } + + Texture::SharedPtr Texture::createFromApiHandle(ApiHandle handle, Type type, uint32_t width, uint32_t height, uint32_t depth, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize, uint32_t mipLevels, State initState, BindFlags bindFlags) + { + switch (type) + { + case Resource::Type::Texture1D: + assert(height == 1 && depth == 1 && sampleCount == 1); + break; + case Resource::Type::Texture2D: + assert(depth == 1 && sampleCount == 1); + break; + case Resource::Type::Texture2DMultisample: + assert(depth == 1); + break; + case Resource::Type::Texture3D: + assert(sampleCount == 1); + break; + case Resource::Type::TextureCube: + assert(depth == 1 && sampleCount == 1); + break; + } + Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, depth, arraySize, mipLevels, sampleCount, format, type, bindFlags)); + pTexture->mApiHandle = handle; + + pTexture->mState.global = initState; + pTexture->mState.isGlobal = true; + return pTexture->mApiHandle ? pTexture : nullptr; + } + + Texture::SharedPtr Texture::create1D(uint32_t width, ResourceFormat format, uint32_t arraySize, uint32_t mipLevels, const void* pData, BindFlags bindFlags) + { + bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels, format, "Texture1D"); + Texture::SharedPtr pTexture = SharedPtr(new Texture(width, 1, 1, arraySize, mipLevels, 1, format, Type::Texture1D, bindFlags)); + pTexture->apiInit(pData, (mipLevels == kMaxPossible)); + return pTexture->mApiHandle ? pTexture : nullptr; + } + + Texture::SharedPtr Texture::create2D(uint32_t width, uint32_t height, ResourceFormat format, uint32_t arraySize, uint32_t mipLevels, const void* pData, BindFlags bindFlags) + { + bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels, format, "Texture2D"); + Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, 1, arraySize, mipLevels, 1, format, Type::Texture2D, bindFlags)); + pTexture->apiInit(pData, (mipLevels == kMaxPossible)); + return pTexture->mApiHandle ? pTexture : nullptr; + } + + Texture::SharedPtr Texture::create3D(uint32_t width, uint32_t height, uint32_t depth, ResourceFormat format, uint32_t mipLevels, const void* pData, BindFlags bindFlags, bool isSparse) + { + bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels, format, "Texture3D"); + Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, depth, 1, mipLevels, 1, format, Type::Texture3D, bindFlags)); + pTexture->apiInit(pData, (mipLevels == kMaxPossible)); + return pTexture->mApiHandle ? pTexture : nullptr; + } + + // Texture Cube + Texture::SharedPtr Texture::createCube(uint32_t width, uint32_t height, ResourceFormat format, uint32_t arraySize, uint32_t mipLevels, const void* pData, BindFlags bindFlags) + { + bindFlags = updateBindFlags(bindFlags, pData != nullptr, mipLevels, format, "TextureCube"); + Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, 1, arraySize, mipLevels, 1, format, Type::TextureCube, bindFlags)); + pTexture->apiInit(pData, (mipLevels == kMaxPossible)); + return pTexture->mApiHandle ? pTexture : nullptr; + } + + Texture::SharedPtr Texture::create2DMS(uint32_t width, uint32_t height, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize, BindFlags bindFlags) + { + bindFlags = updateBindFlags(bindFlags, false, 1, format, "Texture2DMultisample"); + Texture::SharedPtr pTexture = SharedPtr(new Texture(width, height, 1, arraySize, 1, sampleCount, format, Type::Texture2DMultisample, bindFlags)); + pTexture->apiInit(nullptr, false); + return pTexture->mApiHandle ? pTexture : nullptr; + } + + Texture::Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, uint32_t sampleCount, ResourceFormat format, Type type, BindFlags bindFlags) + : Resource(type, bindFlags, 0), mWidth(width), mHeight(height), mDepth(depth), mMipLevels(mipLevels), mSampleCount(sampleCount), mArraySize(arraySize), mFormat(format) + { + if(mMipLevels == kMaxPossible) + { + uint32_t dims = width | height | depth; + mMipLevels = bitScanReverse(dims) + 1; + } + mState.perSubresource.resize(mMipLevels * mArraySize, mState.global); + } + + template + using CreateFuncType = std::function; + + template + typename ViewClass::SharedPtr findViewCommon(Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize, ViewMapType& viewMap, CreateFuncType createFunc) + { + uint32_t resMipCount = 1; + uint32_t resArraySize = 1; + + resArraySize = pTexture->getArraySize(); + resMipCount = pTexture->getMipCount(); + + if (firstArraySlice >= resArraySize) + { + logWarning("First array slice is OOB when creating resource view. Clamping"); + firstArraySlice = resArraySize - 1; + } + + if (mostDetailedMip >= resMipCount) + { + logWarning("Most detailed mip is OOB when creating resource view. Clamping"); + mostDetailedMip = resMipCount - 1; + } + + if (mipCount == Resource::kMaxPossible) + { + mipCount = resMipCount - mostDetailedMip; + } + else if (mipCount + mostDetailedMip > resMipCount) + { + logWarning("Mip count is OOB when creating resource view. Clamping"); + mipCount = resMipCount - mostDetailedMip; + } + + if (arraySize == Resource::kMaxPossible) + { + arraySize = resArraySize - firstArraySlice; + } + else if (arraySize + firstArraySlice > resArraySize) + { + logWarning("Array size is OOB when creating resource view. Clamping"); + arraySize = resArraySize - firstArraySlice; + } + + ResourceViewInfo view = ResourceViewInfo(mostDetailedMip, mipCount, firstArraySlice, arraySize); + + if (viewMap.find(view) == viewMap.end()) + { + viewMap[view] = createFunc(pTexture, mostDetailedMip, mipCount, firstArraySlice, arraySize); + } + + return viewMap[view]; + } + + DepthStencilView::SharedPtr Texture::getDSV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + auto createFunc = [](Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + { + return DepthStencilView::create(pTexture->shared_from_this(), mostDetailedMip, firstArraySlice, arraySize); + }; + + return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mDsvs, createFunc); + } + + UnorderedAccessView::SharedPtr Texture::getUAV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + auto createFunc = [](Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + { + return UnorderedAccessView::create(pTexture->shared_from_this(), mostDetailedMip, firstArraySlice, arraySize); + }; + + return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mUavs, createFunc); + } + + RenderTargetView::SharedPtr Texture::getRTV(uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) + { + auto createFunc = [](Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + { + return RenderTargetView::create(pTexture->shared_from_this(), mostDetailedMip, firstArraySlice, arraySize); + }; + + return findViewCommon(this, mipLevel, 1, firstArraySlice, arraySize, mRtvs, createFunc); + } + + ShaderResourceView::SharedPtr Texture::getSRV(uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + { + auto createFunc = [](Texture* pTexture, uint32_t mostDetailedMip, uint32_t mipCount, uint32_t firstArraySlice, uint32_t arraySize) + { + return ShaderResourceView::create(pTexture->shared_from_this(), mostDetailedMip, mipCount, firstArraySlice, arraySize); + }; + + return findViewCommon(this, mostDetailedMip, mipCount, firstArraySlice, arraySize, mSrvs, createFunc); + } + + void Texture::captureToFile(uint32_t mipLevel, uint32_t arraySlice, const std::string& filename, Bitmap::FileFormat format, Bitmap::ExportFlags exportFlags) + { + assert(mType == Type::Texture2D); + RenderContext* pContext = gpDevice->getRenderContext(); + // Handle the special case where we have an HDR texture with less then 3 channels + FormatType type = getFormatType(mFormat); + uint32_t channels = getFormatChannelCount(mFormat); + std::vector textureData; + ResourceFormat resourceFormat = mFormat; + + if (type == FormatType::Float && channels < 3) + { + Texture::SharedPtr pOther = Texture::create2D(getWidth(mipLevel), getHeight(mipLevel), ResourceFormat::RGBA32Float, 1, 1, nullptr, ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource); + pContext->blit(getSRV(mipLevel, 1, arraySlice, 1), pOther->getRTV(0, 0, 1)); + textureData = pContext->readTextureSubresource(pOther.get(), 0); + resourceFormat = ResourceFormat::RGBA32Float; + } + else + { + uint32_t subresource = getSubresourceIndex(arraySlice, mipLevel); + textureData = pContext->readTextureSubresource(this, subresource); + } + + auto func = [=]() + { + Bitmap::saveImage(filename, getWidth(mipLevel), getHeight(mipLevel), format, exportFlags, resourceFormat, true, (void*)textureData.data()); + }; + + Threading::dispatchTask(func); + } + + void Texture::uploadInitData(const void* pData, bool autoGenMips) + { + auto pRenderContext = gpDevice->getRenderContext(); + if (autoGenMips) + { + // Upload just the first mip-level + size_t arraySliceSize = mWidth * mHeight * getFormatBytesPerBlock(mFormat); + const uint8_t* pSrc = (uint8_t*)pData; + uint32_t numFaces = (mType == Texture::Type::TextureCube) ? 6 : 1; + for (uint32_t i = 0; i < mArraySize * numFaces; i++) + { + uint32_t subresource = getSubresourceIndex(i, 0); + pRenderContext->updateSubresourceData(this, subresource, pSrc); + pSrc += arraySliceSize; + } + } + else + { + pRenderContext->updateTextureData(this, pData); + } + + if (autoGenMips) + { + generateMips(gpDevice->getRenderContext()); + invalidateViews(); + } + } + + void Texture::generateMips(RenderContext* pContext) + { + if (mType != Type::Texture2D) + { + logWarning("Texture::generateMips() was only tested with Texture2Ds"); + } + // #OPTME: should blit support arrays? + for (uint32_t m = 0; m < mMipLevels - 1; m++) + { + for(uint32_t a = 0 ; a < mArraySize ; a++) + { + auto srv = getSRV(m, 1, a, 1); + auto rtv = getRTV(m + 1, a, 1); + pContext->blit(srv, rtv); + } + } + + if (mReleaseRtvsAfterGenMips) + { + // Releasing RTVs to free space on the heap. + // We only do it once to handle the case that generateMips() was called during load. + // If it was called more then once, the texture is probably dynamic and it's better to keep the RTVs around + mRtvs.clear(); + mReleaseRtvsAfterGenMips = false; + } + } + + uint32_t Texture::getTextureSizeInBytes() + { + ID3D12DevicePtr pDevicePtr = gpDevice->getApiHandle(); + ID3D12ResourcePtr pTexResource = this->getApiHandle(); + + D3D12_RESOURCE_ALLOCATION_INFO d3d12ResourceAllocationInfo; + D3D12_RESOURCE_DESC desc = pTexResource->GetDesc(); + + assert(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D); + assert(desc.Width == mWidth); + assert(desc.Height == mHeight); + + d3d12ResourceAllocationInfo = pDevicePtr->GetResourceAllocationInfo(0, 1, &desc); + return (uint32_t)d3d12ResourceAllocationInfo.SizeInBytes; + } + + SCRIPT_BINDING(Texture) + { + auto c = m.regClass(Texture); + c.func_("width", &Texture::getWidth); + c.func_("height", &Texture::getHeight); + c.func_("depth", &Texture::getDepth); + c.func_("mipCount", &Texture::getMipCount); + c.func_("arraySize", &Texture::getArraySize); + c.func_("samples", &Texture::getSampleCount); + c.func_("format", &Texture::getFormat); + + auto data = [](Texture* pTexture, uint32_t subresource) + { + return gpDevice->getRenderContext()->readTextureSubresource(pTexture, subresource); + }; + c.func_("data", data); + } +} diff --git a/Framework/Source/API/Texture.h b/Source/Falcor/Core/API/Texture.h similarity index 75% rename from Framework/Source/API/Texture.h rename to Source/Falcor/Core/API/Texture.h index bfc007cd5..65a495703 100644 --- a/Framework/Source/API/Texture.h +++ b/Source/Falcor/Core/API/Texture.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,9 +27,8 @@ ***************************************************************************/ #pragma once #include -#include "API/Formats.h" #include "Resource.h" -#include "Utils/Bitmap.h" +#include "Utils/Image/Bitmap.h" namespace Falcor { @@ -39,11 +38,12 @@ namespace Falcor /** Abstracts the API texture objects */ - class Texture : public Resource, public inherit_shared_from_this + class dlldecl Texture : public Resource, public inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; using WeakPtr = std::weak_ptr; using WeakConstPtr = std::weak_ptr; using inherit_shared_from_this::shared_from_this; @@ -151,9 +151,45 @@ namespace Falcor \param bindFlags The requested bind flags for the resource \return A pointer to a new texture, or nullptr if creation failed */ - static SharedPtr create2DMS(uint32_t width, uint32_t height, ResourceFormat format, uint32_t sampleCount, uint32_t arraySize = 1, BindFlags bindFlags = BindFlags::ShaderResource); + /** Create a new texture object from a file. + \param[in] filename Filename of the image. Can also include a full path or relative path from a data directory + \param[in] generateMipLevels Whether the mip-chain should be generated + \param[in] loadAsSrgb Load the texture using sRGB format. Only valid for 3 or 4 component textures. + \param[in] bindFlags The bind flags to create the texture with + */ + static SharedPtr createFromFile(const std::string& filename, bool generateMipLevels, bool loadAsSrgb, BindFlags bindFlags = BindFlags::ShaderResource); + + /** Get a shader-resource view. + \param[in] mostDetailedMip The most detailed mip level of the view + \param[in] mipCount The number of mip-levels to bind. If this is equal to Texture#kMaxPossible, will create a view ranging from mostDetailedMip to the texture's mip levels count + \param[in] firstArraySlice The first array slice of the view + \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size + */ + ShaderResourceView::SharedPtr getSRV(uint32_t mostDetailedMip = 0, uint32_t mipCount = kMaxPossible, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + + /** Get a render-target view. + \param[in] mipLevel The requested mip-level + \param[in] firstArraySlice The first array slice of the view + \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size + */ + RenderTargetView::SharedPtr getRTV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + + /** Get a depth stencil view. + \param[in] mipLevel The requested mip-level + \param[in] firstArraySlice The first array slice of the view + \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size + */ + DepthStencilView::SharedPtr getDSV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + + /** Get an unordered access view. + \param[in] mipLevel The requested mip-level + \param[in] firstArraySlice The first array slice of the view + \param[in] arraySize The array size. If this is equal to Texture#kMaxPossible, will create a view ranging from firstArraySlice to the texture's array size + */ + UnorderedAccessView::SharedPtr getUAV(uint32_t mipLevel = 0, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + /** Capture the texture to an image file. \param[in] mipLevel Requested mip-level \param[in] arraySlice Requested array-slice @@ -161,7 +197,7 @@ namespace Falcor \param[in] fileFormat Destination image file format (e.g., PNG, PFM, etc.) \param[in] exportFlags Save flags, see Bitmap::ExportFlags */ - void captureToFile(uint32_t mipLevel, uint32_t arraySlice, const std::string& filename, Bitmap::FileFormat format = Bitmap::FileFormat::PngFile, Bitmap::ExportFlags exportFlags = Bitmap::ExportFlags::None) const; + void captureToFile(uint32_t mipLevel, uint32_t arraySlice, const std::string& filename, Bitmap::FileFormat format = Bitmap::FileFormat::PngFile, Bitmap::ExportFlags exportFlags = Bitmap::ExportFlags::None); /** Generates mipmaps for a specified texture object. */ @@ -175,15 +211,15 @@ namespace Falcor */ const std::string& getSourceFilename() const { return mSourceFilename; } + /** Returns the size of the texture in bytes as allocated in GPU memory. + */ + uint32_t getTextureSizeInBytes(); + protected: friend class Device; - void apinit(const void* pData, bool autoGenMips); + void apiInit(const void* pData, bool autoGenMips); void uploadInitData(const void* pData, bool autoGenMips); - bool mReleaseRtvsAfterGenMips = true; - static RtvHandle spNullRTV; - static DsvHandle spNullDSV; - - static uint32_t tempDefaultUint; + bool mReleaseRtvsAfterGenMips = true; std::string mSourceFilename; diff --git a/Framework/Source/Graphics/TextureHelper.cpp b/Source/Falcor/Core/API/TextureLoader.cpp similarity index 98% rename from Framework/Source/Graphics/TextureHelper.cpp rename to Source/Falcor/Core/API/TextureLoader.cpp index 0455d41d0..65b010e22 100644 --- a/Framework/Source/Graphics/TextureHelper.cpp +++ b/Source/Falcor/Core/API/TextureLoader.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "TextureHelper.h" -#include "API/Texture.h" -#include "Utils/Bitmap.h" -#include "Utils/DDSHeader.h" +#include "stdafx.h" +#include "Core/API/Texture.h" +#include "Utils/Image/DDSHeader.h" #include "Utils/BinaryFileStream.h" #include "Utils/StringUtils.h" #include @@ -509,7 +507,7 @@ namespace Falcor { uint32_t heightPitch = max(width >> mipCounter, 1U) * getFormatBytesPerBlock(format); uint32_t currentMipHeight = max(height >> mipCounter, 1U); - uint32_t depthPitch = currentMipHeight * heightPitch; + uint32_t depthPitch = currentMipHeight * heightPitch; for (uint32_t depthCounter = 0; depthCounter < depth; ++depthCounter) { @@ -697,7 +695,7 @@ namespace Falcor return nullptr; } - Texture::SharedPtr createTextureFromFile(const std::string& filename, bool generateMipLevels, bool loadAsSrgb, Texture::BindFlags bindFlags) + Texture::SharedPtr Texture::createFromFile(const std::string& filename, bool generateMipLevels, bool loadAsSrgb, Texture::BindFlags bindFlags) { #define no_srgb() \ if(loadAsSrgb) \ diff --git a/Framework/Source/API/VAO.cpp b/Source/Falcor/Core/API/VAO.cpp similarity index 91% rename from Framework/Source/API/VAO.cpp rename to Source/Falcor/Core/API/VAO.cpp index 86612b259..c89f8a479 100644 --- a/Framework/Source/API/VAO.cpp +++ b/Source/Falcor/Core/API/VAO.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,20 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "VAO.h" namespace Falcor { bool checkVaoParams(const Vao::BufferVec& vbDesc, const VertexLayout* pLayout, Buffer* pIB, ResourceFormat ibFormat) { - if (pLayout->getBufferCount() != vbDesc.size()) - { - logError("Error when creating VAO. Number of buffers in the BufferVec is different then the number of buffers in the vertex layout object"); - return false; - - } - if(pIB) { if (ibFormat != ResourceFormat::R16Uint && ibFormat != ResourceFormat::R32Uint) @@ -102,4 +95,8 @@ namespace Falcor return desc; } -} \ No newline at end of file + SCRIPT_BINDING(Vao) + { + m.regClass(Vao); + } +} diff --git a/Framework/Source/API/VAO.h b/Source/Falcor/Core/API/VAO.h similarity index 92% rename from Framework/Source/API/VAO.h rename to Source/Falcor/Core/API/VAO.h index bc2896c2d..a2a961eca 100644 --- a/Framework/Source/API/VAO.h +++ b/Source/Falcor/Core/API/VAO.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,14 +28,13 @@ #pragma once #include #include "VertexLayout.h" -#include "Buffer.h" namespace Falcor { /** Abstracts vertex array objects. A VAO must at least specify a primitive topology. You may additionally specify a number of Vertex buffer layouts corresponding to the number of vertex buffers to be bound. The number of vertex buffers to be bound must match the number described in the layout. */ - class Vao : public std::enable_shared_from_this + class dlldecl Vao : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -46,6 +45,7 @@ namespace Falcor */ enum class Topology { + Undefined, PointList, LineList, LineStrip, @@ -69,7 +69,7 @@ namespace Falcor \param pIB Pointer to the index-buffer. Can be nullptr, in which case no index-buffer will be bound. \param ibFormat The resource format of the index buffer. Can be either R16Uint or R32Uint */ - static SharedPtr create(Topology primTopology, const VertexLayout::SharedPtr& pLayout = VertexLayout::SharedPtr(), const BufferVec& pVBs = BufferVec(), const Buffer::SharedPtr& pIB = Buffer::SharedPtr(), ResourceFormat ibFormat = ResourceFormat::Unknown); + static SharedPtr create(Topology primTopology, const VertexLayout::SharedPtr& pLayout = nullptr, const BufferVec& pVBs = BufferVec(), const Buffer::SharedPtr& pIB = nullptr, ResourceFormat ibFormat = ResourceFormat::Unknown); ~Vao(); /** Get the API handle @@ -95,7 +95,7 @@ namespace Falcor /** Get the index buffer */ - Buffer::SharedPtr getIndexBuffer() const { return mpIB; } + const Buffer::SharedPtr& getIndexBuffer() const { return mpIB; } /** Get the index buffer format */ @@ -119,4 +119,4 @@ namespace Falcor ResourceFormat mIbFormat; Topology mTopology; }; -} \ No newline at end of file +} diff --git a/Samples/Utils/LightProbeViewer/Data/LightProbeViewer.ps.hlsl b/Source/Falcor/Core/API/VertexLayout.cpp similarity index 88% rename from Samples/Utils/LightProbeViewer/Data/LightProbeViewer.ps.hlsl rename to Source/Falcor/Core/API/VertexLayout.cpp index ee9c4c8c6..b3b808712 100644 --- a/Samples/Utils/LightProbeViewer/Data/LightProbeViewer.ps.hlsl +++ b/Source/Falcor/Core/API/VertexLayout.cpp @@ -25,11 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import DefaultVS; -__import Shading; +#include "stdafx.h" +#include "VertexLayout.h" -float4 main(VertexOut vOut) : SV_TARGET +namespace Falcor { - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); - return float4(evalMaterial(sd, gLightProbe).color.rgb, 1); + SCRIPT_BINDING(VertexLayout) + { + m.regClass(VertexLayout); + } } diff --git a/Framework/Source/API/VertexLayout.h b/Source/Falcor/Core/API/VertexLayout.h similarity index 84% rename from Framework/Source/API/VertexLayout.h rename to Source/Falcor/Core/API/VertexLayout.h index 17b5f2967..931846a84 100644 --- a/Framework/Source/API/VertexLayout.h +++ b/Source/Falcor/Core/API/VertexLayout.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,17 +27,14 @@ ***************************************************************************/ #pragma once #include -#include "Formats.h" #include "Data/VertexAttrib.h" -#include "Graphics/Program/Program.h" +#include "Core/Program/Program.h" namespace Falcor { - class Program; - /** Describes the layout of a vertex buffer that will be bound to a render operation as part of a VAO. */ - class VertexBufferLayout : public std::enable_shared_from_this + class dlldecl VertexBufferLayout : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -195,12 +192,7 @@ namespace Falcor */ void addVertexAttribDclToProg(Program* pProg) const { - pProg->removeDefine("HAS_NORMAL"); - pProg->removeDefine("HAS_BITANGENT"); - pProg->removeDefine("HAS_TEXCRD"); - pProg->removeDefine("HAS_COLORS"); pProg->removeDefine("HAS_LIGHTMAP_UV"); - pProg->removeDefine("HAS_PREV_POSITION"); for (const auto& l : mpBufferLayouts) { @@ -208,30 +200,10 @@ namespace Falcor { for (uint32_t i = 0; i < l->getElementCount(); i++) { - if (l->getElementShaderLocation(i) == VERTEX_NORMAL_LOC) - { - pProg->addDefine("HAS_NORMAL"); - } - if (l->getElementShaderLocation(i) == VERTEX_BITANGENT_LOC) - { - pProg->addDefine("HAS_BITANGENT"); - } - if (l->getElementShaderLocation(i) == VERTEX_TEXCOORD_LOC) - { - pProg->addDefine("HAS_TEXCRD"); - } if (l->getElementShaderLocation(i) == VERTEX_LIGHTMAP_UV_LOC) { pProg->addDefine("HAS_LIGHTMAP_UV"); } - if (l->getElementShaderLocation(i) == VERTEX_DIFFUSE_COLOR_LOC) - { - pProg->addDefine("HAS_COLORS"); - } - if (l->getElementShaderLocation(i) == VERTEX_PREV_POSITION_LOC) - { - pProg->addDefine("HAS_PREV_POSITION"); - } } } } @@ -241,4 +213,4 @@ namespace Falcor VertexLayout() { mpBufferLayouts.reserve(16); } std::vector mpBufferLayouts; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/Vulkan/FalcorVK.h b/Source/Falcor/Core/API/Vulkan/FalcorVK.h similarity index 98% rename from Framework/Source/API/Vulkan/FalcorVK.h rename to Source/Falcor/Core/API/Vulkan/FalcorVK.h index efa4ecec5..2570b5c9e 100644 --- a/Framework/Source/API/Vulkan/FalcorVK.h +++ b/Source/Falcor/Core/API/Vulkan/FalcorVK.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -130,7 +130,7 @@ namespace Falcor #define VK_FAILED(res) (res != VK_SUCCESS) -#ifdef _LOG_ENABLED +#if _LOG_ENABLED #define vk_call(a) {auto r = a; if(VK_FAILED(r)) { logError("Vulkan call failed.\n"#a); }} #else #define vk_call(a) a diff --git a/Framework/Source/API/Vulkan/VKBuffer.cpp b/Source/Falcor/Core/API/Vulkan/VKBuffer.cpp similarity index 93% rename from Framework/Source/API/Vulkan/VKBuffer.cpp rename to Source/Falcor/Core/API/Vulkan/VKBuffer.cpp index 5a285e413..22113e141 100644 --- a/Framework/Source/API/Vulkan/VKBuffer.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKBuffer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Buffer.h" #include "API/Device.h" #include "API/LowLevel/ResourceAllocator.h" @@ -34,7 +34,7 @@ namespace Falcor { - VkDeviceMemory allocateDeviceMemory(Device::MemoryType memType, uint32_t memoryTypeBits, size_t size) + VkDeviceMemory allocateDeviceMemory(GpuMemoryHeap::Type memType, uint32_t memoryTypeBits, size_t size) { VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; @@ -75,7 +75,7 @@ namespace Falcor return reqs.alignment; } - Buffer::ApiHandle createBuffer(size_t size, Buffer::BindFlags bindFlags, Device::MemoryType memType) + Buffer::ApiHandle createBuffer(size_t size, Buffer::BindFlags bindFlags, GpuMemoryHeap::Type memType) { VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -90,8 +90,8 @@ namespace Falcor vk_call(vkCreateBuffer(gpDevice->getApiHandle(), &bufferInfo, nullptr, &buffer)); // Get the required buffer size - VkMemoryRequirements reqs; - vkGetBufferMemoryRequirements(gpDevice->getApiHandle(), buffer, &reqs); + VkMemoryRequirements reqs; + vkGetBufferMemoryRequirements(gpDevice->getApiHandle(), buffer, &reqs); VkDeviceMemory mem = allocateDeviceMemory(memType, reqs.memoryTypeBits, reqs.size); vk_call(vkBindBufferMemory(gpDevice->getApiHandle(), buffer, mem, 0)); @@ -104,7 +104,7 @@ namespace Falcor { if (mCpuAccess == CpuAccess::Write) { - mDynamicData = gpDevice->getResourceAllocator()->allocate(mSize); + mDynamicData = gpDevice->getUploadHeap()->allocate(mSize); mApiHandle = mDynamicData.pResourceHandle; } else diff --git a/Framework/Source/API/Vulkan/VKComputeContext.cpp b/Source/Falcor/Core/API/Vulkan/VKComputeContext.cpp similarity index 93% rename from Framework/Source/API/Vulkan/VKComputeContext.cpp rename to Source/Falcor/Core/API/Vulkan/VKComputeContext.cpp index 9b7133f64..7480c8e32 100644 --- a/Framework/Source/API/Vulkan/VKComputeContext.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKComputeContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/ComputeContext.h" #include "API/Device.h" #include "API/DescriptorSet.h" @@ -33,7 +33,10 @@ namespace Falcor { - void ComputeContext::prepareForDispatch() + ComputeContext::ComputeContext() = default; + ComputeContext::~ComputeContext() = default; + + bool ComputeContext::prepareForDispatch() { assert(mpComputeState); if(mpComputeVars) applyComputeVars(); @@ -109,13 +112,13 @@ namespace Falcor void ComputeContext::dispatch(uint32_t groupSizeX, uint32_t groupSizeY, uint32_t groupSizeZ) { - prepareForDispatch(); + if (prepareForDispatch(pState, pVars) == false) return; vkCmdDispatch(mpLowLevelData->getCommandList(), groupSizeX, groupSizeY, groupSizeZ); } void ComputeContext::dispatchIndirect(const Buffer* pArgBuffer, uint64_t argBufferOffset) { - prepareForDispatch(); + if (prepareForDispatch(pState, pVars) == false) return; resourceBarrier(pArgBuffer, Resource::State::IndirectArg); vkCmdDispatchIndirect(mpLowLevelData->getCommandList(), pArgBuffer->getApiHandle(), pArgBuffer->getGpuAddressOffset() + argBufferOffset); } diff --git a/Framework/Source/API/Vulkan/VKComputeStateObject.cpp b/Source/Falcor/Core/API/Vulkan/VKComputeStateObject.cpp similarity index 96% rename from Framework/Source/API/Vulkan/VKComputeStateObject.cpp rename to Source/Falcor/Core/API/Vulkan/VKComputeStateObject.cpp index 304250ce6..2f0073563 100644 --- a/Framework/Source/API/Vulkan/VKComputeStateObject.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKComputeStateObject.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/ComputeStateObject.h" #include "VKState.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/VKCopyContext.cpp b/Source/Falcor/Core/API/Vulkan/VKCopyContext.cpp similarity index 97% rename from Framework/Source/API/Vulkan/VKCopyContext.cpp rename to Source/Falcor/Core/API/Vulkan/VKCopyContext.cpp index b54566174..606984b5d 100644 --- a/Framework/Source/API/Vulkan/VKCopyContext.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKCopyContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/CopyContext.h" #include "API/Buffer.h" #include "API/Texture.h" @@ -37,7 +37,7 @@ namespace Falcor { VkImageAspectFlags flags = 0; if (isDepthFormat(format)) flags |= VK_IMAGE_ASPECT_DEPTH_BIT; - if(ignoreStencil == false) + if (ignoreStencil == false) { if (isStencilFormat(format)) flags |= VK_IMAGE_ASPECT_STENCIL_BIT; } @@ -69,7 +69,7 @@ namespace Falcor case Resource::State::UnorderedAccess: return VK_IMAGE_LAYOUT_GENERAL; case Resource::State::RenderTarget: - return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; case Resource::State::DepthStencil: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; case Resource::State::ShaderResource: @@ -85,7 +85,7 @@ namespace Falcor return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; default: should_not_get_here(); - return VkImageLayout (-1); + return VkImageLayout(-1); } } @@ -273,14 +273,14 @@ namespace Falcor vkCmdPipelineBarrier(mpLowLevelData->getCommandList(), getShaderStageMask(oldState, true), getShaderStageMask(newState, false), 0, 0, nullptr, 0, nullptr, 1, &barrier); } - void CopyContext::textureBarrier(const Texture* pTexture, Resource::State newState) + bool CopyContext::textureBarrier(const Texture* pTexture, Resource::State newState) { assert(pTexture->getApiHandle().getType() == VkResourceType::Image); VkImageLayout srcLayout = getImageLayout(pTexture->getGlobalState()); VkImageLayout dstLayout = getImageLayout(newState); - if(srcLayout != dstLayout) + if (srcLayout != dstLayout) { VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -301,10 +301,12 @@ namespace Falcor pTexture->setGlobalState(newState); mCommandsPending = true; + return true; } + return false; } - void CopyContext::bufferBarrier(const Buffer* pBuffer, Resource::State newState) + bool CopyContext::bufferBarrier(const Buffer* pBuffer, Resource::State newState) { assert(pBuffer); assert(pBuffer->getApiHandle().getType() == VkResourceType::Buffer); @@ -326,7 +328,9 @@ namespace Falcor pBuffer->setGlobalState(newState); mCommandsPending = true; + return true; } + return false; } void CopyContext::copyResource(const Resource* pDst, const Resource* pSrc) @@ -437,7 +441,7 @@ namespace Falcor region.extent.width = (size.x == -1) ? pSrc->getWidth(mipLevel) - srcOffset.x : size.x; region.extent.height = (size.y == -1) ? pSrc->getHeight(mipLevel) - srcOffset.y : size.y; region.extent.depth = (size.z == -1) ? pSrc->getDepth(mipLevel) - srcOffset.z : size.z; - + vkCmdCopyImage(mpLowLevelData->getCommandList(), pSrc->getApiHandle(), getImageLayout(Resource::State::CopySource), pDst->getApiHandle(), getImageLayout(Resource::State::CopyDest), 1, ®ion); mCommandsPending = true; diff --git a/Framework/Source/API/Vulkan/LowLevel/VKDescriptorData.h b/Source/Falcor/Core/API/Vulkan/VKDescriptorData.h similarity index 97% rename from Framework/Source/API/Vulkan/LowLevel/VKDescriptorData.h rename to Source/Falcor/Core/API/Vulkan/VKDescriptorData.h index bde0a54fa..632f56f67 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKDescriptorData.h +++ b/Source/Falcor/Core/API/Vulkan/VKDescriptorData.h @@ -1,6 +1,6 @@ #pragma once /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/API/Vulkan/LowLevel/VKDescriptorPool.cpp b/Source/Falcor/Core/API/Vulkan/VKDescriptorPool.cpp similarity index 95% rename from Framework/Source/API/Vulkan/LowLevel/VKDescriptorPool.cpp rename to Source/Falcor/Core/API/Vulkan/VKDescriptorPool.cpp index 6a9848eb7..f6b523fd5 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKDescriptorPool.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKDescriptorPool.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/LowLevel/DescriptorPool.h" #include "API/Device.h" #include "API/Vulkan/LowLevel/VKDescriptorData.h" @@ -40,13 +40,15 @@ namespace Falcor return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; case DescriptorPool::Type::TextureUav: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + case DescriptorPool::Type::RawBufferSrv: case DescriptorPool::Type::TypedBufferSrv: return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + case DescriptorPool::Type::RawBufferUav: case DescriptorPool::Type::TypedBufferUav: return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; case DescriptorPool::Type::Cbv: - case DescriptorPool::Type::StructuredBufferSrv: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case DescriptorPool::Type::StructuredBufferSrv: case DescriptorPool::Type::StructuredBufferUav: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; case DescriptorPool::Type::Dsv: @@ -99,4 +101,4 @@ namespace Falcor { return mpApiData->descriptorPool; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/Vulkan/LowLevel/VKDescriptorSet.cpp b/Source/Falcor/Core/API/Vulkan/VKDescriptorSet.cpp similarity index 98% rename from Framework/Source/API/Vulkan/LowLevel/VKDescriptorSet.cpp rename to Source/Falcor/Core/API/Vulkan/VKDescriptorSet.cpp index 7d0b123e4..dfc0005b5 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKDescriptorSet.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKDescriptorSet.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/DescriptorSet.h" #include "VKDescriptorData.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/VKDevice.cpp b/Source/Falcor/Core/API/Vulkan/VKDevice.cpp similarity index 98% rename from Framework/Source/API/Vulkan/VKDevice.cpp rename to Source/Falcor/Core/API/Vulkan/VKDevice.cpp index 321ec0028..92b8ed620 100644 --- a/Framework/Source/API/Vulkan/VKDevice.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKDevice.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Device.h" #include "API/LowLevel/DescriptorPool.h" #include "API/LowLevel/GpuFence.h" @@ -141,7 +141,7 @@ namespace Falcor } // Get the back-buffer - mCurrentBackBufferIndex = getCurrentBackBufferIndex(mApiHandle, mSwapChainBufferCount, mpApiData); + mCurrentBackBufferIndex = getCurrentBackBufferIndex(mApiHandle, kSwapChainBuffersCount, mpApiData); return true; } @@ -628,7 +628,7 @@ namespace Falcor info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.surface = mApiHandle; uint32 maxImageCount = surfaceCapabilities.maxImageCount ? surfaceCapabilities.maxImageCount : UINT32_MAX; // 0 means no limit on the number of images - info.minImageCount = clamp(kDefaultSwapChainBuffers, surfaceCapabilities.minImageCount, maxImageCount); + info.minImageCount = clamp(kSwapChainBuffersCount, surfaceCapabilities.minImageCount, maxImageCount); info.imageFormat = requestedFormat; info.imageColorSpace = requestedColorSpace; info.imageExtent = { swapchainExtent.width, swapchainExtent.height }; @@ -649,7 +649,9 @@ namespace Falcor return false; } - vkGetSwapchainImagesKHR(mApiHandle, mpApiData->swapchain, &mSwapChainBufferCount, nullptr); + uint32_t swapChainCount = 0; + vkGetSwapchainImagesKHR(mApiHandle, mpApiData->swapchain, &swapChainCount, nullptr); + assert(swapChainCount == kSwapChainBuffersCount); return true; } @@ -661,7 +663,7 @@ namespace Falcor info.pSwapchains = &mpApiData->swapchain; info.pImageIndices = &mCurrentBackBufferIndex; vk_call(vkQueuePresentKHR(mpRenderContext->getLowLevelData()->getCommandQueue(), &info)); - mCurrentBackBufferIndex = getCurrentBackBufferIndex(mApiHandle, mSwapChainBufferCount, mpApiData); + mCurrentBackBufferIndex = getCurrentBackBufferIndex(mApiHandle, kSwapChainBuffersCount, mpApiData); } bool Device::apiInit(const Desc& desc) @@ -685,7 +687,7 @@ namespace Falcor return false; } - mpApiData->presentFences.f.resize(mSwapChainBufferCount); + mpApiData->presentFences.f.resize(kSwapChainBuffersCount); for (auto& f : mpApiData->presentFences.f) { VkFenceCreateInfo info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; @@ -693,8 +695,6 @@ namespace Falcor vk_call(vkCreateFence(device, &info, nullptr, &f)); } - mpRenderContext = RenderContext::create(mCmdQueues[(uint32_t)LowLevelContextData::CommandQueueType::Direct][0]); - return true; } @@ -720,7 +720,7 @@ namespace Falcor return mpApiData->falcorToVulkanQueueType[(uint32_t)type]; } - uint32_t Device::getVkMemoryType(MemoryType falcorType, uint32_t memoryTypeBits) const + uint32_t Device::getVkMemoryType(GpuMemoryHeap::Type falcorType, uint32_t memoryTypeBits) const { uint32_t mask = mpApiData->vkMemoryTypeBits[(uint32_t)falcorType] & memoryTypeBits; assert(mask != 0); diff --git a/Framework/Source/API/Vulkan/VKFbo.cpp b/Source/Falcor/Core/API/Vulkan/VKFbo.cpp similarity index 97% rename from Framework/Source/API/Vulkan/VKFbo.cpp rename to Source/Falcor/Core/API/Vulkan/VKFbo.cpp index f5733570c..b1ea1d6d2 100644 --- a/Framework/Source/API/Vulkan/VKFbo.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKFbo.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/FBO.h" #include "VKState.h" #include "API/Device.h" @@ -44,7 +44,7 @@ namespace Falcor const Fbo::ApiHandle& Fbo::getApiHandle() const { - checkStatus(); + finalize(); return mApiHandle; } diff --git a/Framework/Source/API/Vulkan/VKFormats.cpp b/Source/Falcor/Core/API/Vulkan/VKFormats.cpp similarity index 99% rename from Framework/Source/API/Vulkan/VKFormats.cpp rename to Source/Falcor/Core/API/Vulkan/VKFormats.cpp index 3a4160b62..d33871af9 100644 --- a/Framework/Source/API/Vulkan/VKFormats.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKFormats.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Device.h" namespace Falcor diff --git a/Framework/Source/API/Vulkan/LowLevel/VKGpuFence.cpp b/Source/Falcor/Core/API/Vulkan/VKGpuFence.cpp similarity index 99% rename from Framework/Source/API/Vulkan/LowLevel/VKGpuFence.cpp rename to Source/Falcor/Core/API/Vulkan/VKGpuFence.cpp index 268a5d40b..a7e964b96 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKGpuFence.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKGpuFence.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/LowLevel/GpuFence.h" #include "API/Device.h" #include "API/Vulkan/FalcorVK.h" diff --git a/Framework/Source/API/Vulkan/VKGpuTimer.cpp b/Source/Falcor/Core/API/Vulkan/VKGpuTimer.cpp similarity index 96% rename from Framework/Source/API/Vulkan/VKGpuTimer.cpp rename to Source/Falcor/Core/API/Vulkan/VKGpuTimer.cpp index 146eaf379..fbd23457f 100644 --- a/Framework/Source/API/Vulkan/VKGpuTimer.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKGpuTimer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/GpuTimer.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/VKGraphicsStateObject.cpp b/Source/Falcor/Core/API/Vulkan/VKGraphicsStateObject.cpp similarity index 94% rename from Framework/Source/API/Vulkan/VKGraphicsStateObject.cpp rename to Source/Falcor/Core/API/Vulkan/VKGraphicsStateObject.cpp index 637adf56c..6d2d5ca13 100644 --- a/Framework/Source/API/Vulkan/VKGraphicsStateObject.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKGraphicsStateObject.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/GraphicsStateObject.h" #include "API/FBO.h" #include "API/Texture.h" @@ -62,8 +62,7 @@ namespace Falcor // Multisample State VkPipelineMultisampleStateCreateInfo multisampleInfo = {}; bool enableSampleFrequency = mDesc.getProgramVersion() ? mDesc.getProgramVersion()->getReflector()->isSampleFrequency() : false; - uint32_t sampleMask = mDesc.getSampleMask(); // getSampleMask returns a temp value but needs to be referenced in VkPipelineMultisampleStateCreateInfo by address - initVkMultiSampleInfo(mDesc.getBlendState().get(), mDesc.getFboDesc(), sampleMask, multisampleInfo, enableSampleFrequency); + initVkMultiSampleInfo(mDesc.getBlendState().get(), mDesc.getFboDesc(), mDesc.getSampleMask(), multisampleInfo, enableSampleFrequency); // Depth Stencil State VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {}; @@ -107,4 +106,4 @@ namespace Falcor return true; } -} +} \ No newline at end of file diff --git a/Framework/Source/API/Vulkan/LowLevel/VKLowLevelContextData.cpp b/Source/Falcor/Core/API/Vulkan/VKLowLevelContextData.cpp similarity index 98% rename from Framework/Source/API/Vulkan/LowLevel/VKLowLevelContextData.cpp rename to Source/Falcor/Core/API/Vulkan/VKLowLevelContextData.cpp index 82a48002f..8a8cc6408 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKLowLevelContextData.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKLowLevelContextData.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/LowLevel/LowLevelContextData.h" #include "API/Vulkan/FalcorVK.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/VKRasterizerState.cpp b/Source/Falcor/Core/API/Vulkan/VKRasterizerState.cpp similarity index 95% rename from Framework/Source/API/Vulkan/VKRasterizerState.cpp rename to Source/Falcor/Core/API/Vulkan/VKRasterizerState.cpp index 7c7e3f81b..7f41648d3 100644 --- a/Framework/Source/API/Vulkan/VKRasterizerState.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKRasterizerState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/RasterizerState.h" namespace Falcor diff --git a/Framework/Source/API/Vulkan/VKRenderContext.cpp b/Source/Falcor/Core/API/Vulkan/VKRenderContext.cpp similarity index 96% rename from Framework/Source/API/Vulkan/VKRenderContext.cpp rename to Source/Falcor/Core/API/Vulkan/VKRenderContext.cpp index d62de13be..9640b3eee 100644 --- a/Framework/Source/API/Vulkan/VKRenderContext.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKRenderContext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/RenderContext.h" #include "API/LowLevel/DescriptorPool.h" #include "API/Device.h" @@ -36,7 +36,10 @@ namespace Falcor { VkImageAspectFlags getAspectFlagsFromFormat(ResourceFormat format); VkImageLayout getImageLayout(Resource::State state); - + + RenderContext::RenderContext() = default; + RenderContext::~RenderContext() = default; + RenderContext::SharedPtr RenderContext::create(CommandQueueHandle queue) { SharedPtr pCtx = SharedPtr(new RenderContext()); @@ -184,7 +187,7 @@ namespace Falcor vkCmdEndRenderPass(cmdBuffer); } - void RenderContext::prepareForDraw() + bool RenderContext::prepareForDraw() { assert(mpGraphicsState); // Vao must be valid so at least primitive topology is known @@ -195,7 +198,7 @@ namespace Falcor { if (mpGraphicsVars) { - applyGraphicsVars(); + if (applyGraphicsVars() == false) return; // Skip the call } } if (is_set(RenderContext::StateBindFlags::PipelineState, mBindFlags)) @@ -231,7 +234,7 @@ namespace Falcor void RenderContext::drawInstanced(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation) { - prepareForDraw(); + if (prepareForDraw(pState, pVars) == false) return; vkCmdDraw(mpLowLevelData->getCommandList(), vertexCount, instanceCount, startVertexLocation, startInstanceLocation); endVkDraw(mpLowLevelData->getCommandList()); } @@ -243,7 +246,7 @@ namespace Falcor void RenderContext::drawIndexedInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation) { - prepareForDraw(); + if (prepareForDraw(pState, pVars) == false) return; vkCmdDrawIndexed(mpLowLevelData->getCommandList(), indexCount, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation); endVkDraw(mpLowLevelData->getCommandList()); } @@ -256,7 +259,7 @@ namespace Falcor void RenderContext::drawIndirect(const Buffer* pArgBuffer, uint64_t argBufferOffset) { resourceBarrier(pArgBuffer, Resource::State::IndirectArg); - prepareForDraw(); + if (prepareForDraw(pState, pVars) == false) return; vkCmdDrawIndirect(mpLowLevelData->getCommandList(), pArgBuffer->getApiHandle(), argBufferOffset + pArgBuffer->getGpuAddressOffset(), 1, 0); endVkDraw(mpLowLevelData->getCommandList()); } @@ -264,15 +267,11 @@ namespace Falcor void RenderContext::drawIndexedIndirect(const Buffer* pArgBuffer, uint64_t argBufferOffset) { resourceBarrier(pArgBuffer, Resource::State::IndirectArg); - prepareForDraw(); + if (prepareForDraw(pState, pVars) == false) return; vkCmdDrawIndexedIndirect(mpLowLevelData->getCommandList(), pArgBuffer->getApiHandle(), argBufferOffset + pArgBuffer->getGpuAddressOffset(), 1, 0); endVkDraw(mpLowLevelData->getCommandList()); } - void RenderContext::initDrawCommandSignatures() - { - } - template void initBlitData(const ViewType* pView, const uvec4& rect, VkImageSubresourceLayers& layer, VkOffset3D offset[offsetCount]) { diff --git a/Framework/Source/API/Vulkan/VKResourceViews.cpp b/Source/Falcor/Core/API/Vulkan/VKResourceViews.cpp similarity index 99% rename from Framework/Source/API/Vulkan/VKResourceViews.cpp rename to Source/Falcor/Core/API/Vulkan/VKResourceViews.cpp index 56cd33421..2b9c926c4 100644 --- a/Framework/Source/API/Vulkan/VKResourceViews.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKResourceViews.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/ResourceViews.h" #include "API/Resource.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/LowLevel/VKRootSignature.cpp b/Source/Falcor/Core/API/Vulkan/VKRootSignature.cpp similarity index 98% rename from Framework/Source/API/Vulkan/LowLevel/VKRootSignature.cpp rename to Source/Falcor/Core/API/Vulkan/VKRootSignature.cpp index decae5451..c12100d31 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKRootSignature.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKRootSignature.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/LowLevel/RootSignature.h" #include "API/Device.h" #include diff --git a/Framework/Source/API/Vulkan/VKSampler.cpp b/Source/Falcor/Core/API/Vulkan/VKSampler.cpp similarity index 96% rename from Framework/Source/API/Vulkan/VKSampler.cpp rename to Source/Falcor/Core/API/Vulkan/VKSampler.cpp index fe450c65a..01f4ffac1 100644 --- a/Framework/Source/API/Vulkan/VKSampler.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKSampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Sampler.h" #include "API/Device.h" #include "VKState.h" diff --git a/Framework/Source/API/Vulkan/VKShader.cpp b/Source/Falcor/Core/API/Vulkan/VKShader.cpp similarity index 96% rename from Framework/Source/API/Vulkan/VKShader.cpp rename to Source/Falcor/Core/API/Vulkan/VKShader.cpp index 27dc2ed69..9374d36c7 100644 --- a/Framework/Source/API/Vulkan/VKShader.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKShader.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Shader.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/VKSmartHandle.h b/Source/Falcor/Core/API/Vulkan/VKSmartHandle.h similarity index 99% rename from Framework/Source/API/Vulkan/VKSmartHandle.h rename to Source/Falcor/Core/API/Vulkan/VKSmartHandle.h index b35118444..601930c06 100644 --- a/Framework/Source/API/Vulkan/VKSmartHandle.h +++ b/Source/Falcor/Core/API/Vulkan/VKSmartHandle.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/API/Vulkan/VKState.cpp b/Source/Falcor/Core/API/Vulkan/VKState.cpp similarity index 99% rename from Framework/Source/API/Vulkan/VKState.cpp rename to Source/Falcor/Core/API/Vulkan/VKState.cpp index 4758bb5fd..68192458f 100644 --- a/Framework/Source/API/Vulkan/VKState.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "VKState.h" #include "API/FBO.h" #include "API/Device.h" diff --git a/Framework/Source/API/Vulkan/VKState.h b/Source/Falcor/Core/API/Vulkan/VKState.h similarity index 98% rename from Framework/Source/API/Vulkan/VKState.h rename to Source/Falcor/Core/API/Vulkan/VKState.h index cc10c25c8..495aa8839 100644 --- a/Framework/Source/API/Vulkan/VKState.h +++ b/Source/Falcor/Core/API/Vulkan/VKState.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/API/Vulkan/VKTexture.cpp b/Source/Falcor/Core/API/Vulkan/VKTexture.cpp similarity index 98% rename from Framework/Source/API/Vulkan/VKTexture.cpp rename to Source/Falcor/Core/API/Vulkan/VKTexture.cpp index 1415d6f02..730901570 100644 --- a/Framework/Source/API/Vulkan/VKTexture.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKTexture.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Texture.h" #include "API/Device.h" #include "API/Resource.h" @@ -129,7 +129,7 @@ namespace Falcor return VkImageTiling(-1); } - void Texture::apinit(const void* pData, bool autoGenMips) + void Texture::apiInit(const void* pData, bool autoGenMips) { VkImageCreateInfo imageInfo = {}; diff --git a/Framework/Source/API/Vulkan/VKVao.cpp b/Source/Falcor/Core/API/Vulkan/VKVao.cpp similarity index 95% rename from Framework/Source/API/Vulkan/VKVao.cpp rename to Source/Falcor/Core/API/Vulkan/VKVao.cpp index 4d29cfdb4..db2b4f3d5 100644 --- a/Framework/Source/API/Vulkan/VKVao.cpp +++ b/Source/Falcor/Core/API/Vulkan/VKVao.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/VAO.h" namespace Falcor diff --git a/Framework/Source/API/Vulkan/LowLevel/VKResourceAllocator.cpp b/Source/Falcor/Core/API/Vulkan/VkGpuMemoryHeap.cpp similarity index 88% rename from Framework/Source/API/Vulkan/LowLevel/VKResourceAllocator.cpp rename to Source/Falcor/Core/API/Vulkan/VkGpuMemoryHeap.cpp index 98b0ac6e6..73961d27b 100644 --- a/Framework/Source/API/Vulkan/LowLevel/VKResourceAllocator.cpp +++ b/Source/Falcor/Core/API/Vulkan/VkGpuMemoryHeap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/LowLevel/ResourceAllocator.h" +#include "stdafx.h" +#include "API/LowLevel/GpuMemoryHeap.h" #include "API/Buffer.h" #include "API/Device.h" namespace Falcor { - Buffer::ApiHandle createBuffer(size_t size, Buffer::BindFlags bindFlags, Device::MemoryType memType); + Buffer::ApiHandle createBuffer(size_t size, Buffer::BindFlags bindFlags, GpuMemoryHeap::Type memType); - void ResourceAllocator::initBasePageData(BaseData& data, size_t size) + void GpuMemoryHeap::initBasePageData(BaseData& data, size_t size) { // Create a buffer - data.pResourceHandle = createBuffer(size, Buffer::BindFlags::Constant | Buffer::BindFlags::Vertex | Buffer::BindFlags::Index, Device::MemoryType::Upload); + data.pResourceHandle = createBuffer(size, Buffer::BindFlags::Constant | Buffer::BindFlags::Vertex | Buffer::BindFlags::Index, mType); data.offset = 0; vk_call(vkMapMemory(gpDevice->getApiHandle(), data.pResourceHandle, 0, VK_WHOLE_SIZE, 0, (void**)&data.pData)); } diff --git a/Framework/Source/API/Vulkan/VkQueryHeap.cpp b/Source/Falcor/Core/API/Vulkan/VkQueryHeap.cpp similarity index 96% rename from Framework/Source/API/Vulkan/VkQueryHeap.cpp rename to Source/Falcor/Core/API/Vulkan/VkQueryHeap.cpp index f05707c51..08c0f4c63 100644 --- a/Framework/Source/API/Vulkan/VkQueryHeap.cpp +++ b/Source/Falcor/Core/API/Vulkan/VkQueryHeap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Device.h" #include "API/QueryHeap.h" diff --git a/Framework/Source/API/Vulkan/VkResource.cpp b/Source/Falcor/Core/API/Vulkan/VkResource.cpp similarity index 95% rename from Framework/Source/API/Vulkan/VkResource.cpp rename to Source/Falcor/Core/API/Vulkan/VkResource.cpp index 748c1d561..17be97ce7 100644 --- a/Framework/Source/API/Vulkan/VkResource.cpp +++ b/Source/Falcor/Core/API/Vulkan/VkResource.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Resource.h" namespace Falcor diff --git a/Framework/Source/API/Vulkan/VkSmartHandle.cpp b/Source/Falcor/Core/API/Vulkan/VkSmartHandle.cpp similarity index 98% rename from Framework/Source/API/Vulkan/VkSmartHandle.cpp rename to Source/Falcor/Core/API/Vulkan/VkSmartHandle.cpp index 1dbca27f1..930076e7b 100644 --- a/Framework/Source/API/Vulkan/VkSmartHandle.cpp +++ b/Source/Falcor/Core/API/Vulkan/VkSmartHandle.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "API/Vulkan/VKSmartHandle.h" #include "API/Device.h" diff --git a/Framework/Source/API/ConstantBuffer.cpp b/Source/Falcor/Core/BufferTypes/ConstantBuffer.cpp similarity index 81% rename from Framework/Source/API/ConstantBuffer.cpp rename to Source/Falcor/Core/BufferTypes/ConstantBuffer.cpp index dda6a75c0..2d8b6a700 100644 --- a/Framework/Source/API/ConstantBuffer.cpp +++ b/Source/Falcor/Core/BufferTypes/ConstantBuffer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,31 +25,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ConstantBuffer.h" -#include "Graphics/Program/ProgramVersion.h" -#include "Buffer.h" -#include "Texture.h" -#include "Graphics/Program/ProgramReflection.h" -#include "API/Device.h" +#include "Core/Program/Program.h" namespace Falcor { ConstantBuffer::~ConstantBuffer() = default; - ConstantBuffer::ConstantBuffer(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t size) : - VariablesBuffer(name, pReflectionType, size, 1, Buffer::BindFlags::Constant, Buffer::CpuAccess::Write) + ConstantBuffer::ConstantBuffer(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t size, bool useDefaultHeap) : + VariablesBuffer(name, pReflectionType, size, 1, Buffer::BindFlags::Constant, useDefaultHeap ? Buffer::CpuAccess::None : Buffer::CpuAccess::Write) { } - ConstantBuffer::SharedPtr ConstantBuffer::create(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t overrideSize) + ConstantBuffer::SharedPtr ConstantBuffer::create(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t overrideSize, bool useDefaultHeap) { size_t size = (overrideSize == 0) ? pReflectionType->getSize() : overrideSize; - SharedPtr pBuffer = SharedPtr(new ConstantBuffer(name, pReflectionType, size)); + SharedPtr pBuffer = SharedPtr(new ConstantBuffer(name, pReflectionType, size, useDefaultHeap)); return pBuffer; } - ConstantBuffer::SharedPtr ConstantBuffer::create(Program::SharedPtr& pProgram, const std::string& name, size_t overrideSize) + ConstantBuffer::SharedPtr ConstantBuffer::create(const Program* pProgram, const std::string& name, size_t overrideSize, bool useDefaultHeap) { const auto& pProgReflector = pProgram->getReflector(); const auto& pParamBlockReflection = pProgReflector->getDefaultParameterBlock(); @@ -60,7 +56,7 @@ namespace Falcor ReflectionResourceType::SharedConstPtr pResType = pBufferReflector->getType()->asResourceType()->inherit_shared_from_this::shared_from_this(); if(pResType && pResType->getType() == ReflectionResourceType::Type::ConstantBuffer) { - return create(name, pResType, overrideSize); + return create(name, pResType, overrideSize, useDefaultHeap); } } logError("Can't find a constant buffer named \"" + name + "\" in the program"); @@ -69,16 +65,13 @@ namespace Falcor bool ConstantBuffer::uploadToGPU(size_t offset, size_t size) { - if (mDirty) mpCbv = nullptr; + if (mCpuAccess == Buffer::CpuAccess::Write && mDirty) mpCbv = nullptr; return VariablesBuffer::uploadToGPU(offset, size); } ConstantBufferView::SharedPtr ConstantBuffer::getCbv() { - if (mpCbv == nullptr) - { - mpCbv = ConstantBufferView::create(Resource::shared_from_this()); - } + if (mpCbv == nullptr) mpCbv = ConstantBufferView::create(Resource::shared_from_this()); return mpCbv; } -} \ No newline at end of file +} diff --git a/Framework/Source/API/ConstantBuffer.h b/Source/Falcor/Core/BufferTypes/ConstantBuffer.h similarity index 82% rename from Framework/Source/API/ConstantBuffer.h rename to Source/Falcor/Core/BufferTypes/ConstantBuffer.h index e690af229..eeb8198e1 100644 --- a/Framework/Source/API/ConstantBuffer.h +++ b/Source/Falcor/Core/BufferTypes/ConstantBuffer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,24 +26,22 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include "Graphics/Program/ProgramReflection.h" -#include "Texture.h" #include "VariablesBuffer.h" -#include "Graphics/Program/Program.h" namespace Falcor { - class Sampler; + class Program; /** Abstracts a Constant/Uniform buffer. When accessing a variable by name, you can only use a name which points to a basic Type, or an array of basic Type (so if you want the start of a structure, ask for the first field in the struct). Note that Falcor has 2 flavors of setting variable by names - SetVariable() and SetVariableArray(). Naming rules for N-dimensional arrays of a basic Type are a little different between the two. SetVariable() must include N indices. SetVariableArray() can include N indices, or N-1 indices (implicit [0] as last index). */ - class ConstantBuffer : public VariablesBuffer, public inherit_shared_from_this + class dlldecl ConstantBuffer : public VariablesBuffer, public inherit_shared_from_this { public: + using inherit_shared_from_this::shared_from_this; + class SharedPtr : public std::shared_ptr { public: @@ -54,6 +52,12 @@ namespace Falcor template void operator=(const T& val) { mpBuf->setVariable(mOffset, val); } size_t getOffset() const { return mOffset; } + + /** Allows mySharedPtr["myIdx1"]["myVar"].setBlob( blobData )... + In theory, block binary transfers could be done with operator=, but without careful coding that could *accidentally* do implicit binary transfers + */ + template + bool setBlob(const T& blob, size_t blobSz = sizeof(T)) { return mpBuf->setBlob(&blob, mOffset, blobSz); } protected: ConstantBuffer* mpBuf; size_t mOffset; @@ -64,8 +68,8 @@ namespace Falcor SharedPtr(std::shared_ptr pBuf) : std::shared_ptr(pBuf) {} constexpr SharedPtr(nullptr_t) : std::shared_ptr(nullptr) {} - Var operator[](size_t offset) { return Var(get(), offset); } - Var operator[](const std::string& var) { return Var(get(), get()->getVariableOffset(var)); } + Var operator[](size_t offset) const { return Var(get(), offset); } + Var operator[](const std::string& var) const { return Var(get(), get()->getVariableOffset(var)); } }; using SharedConstPtr = std::shared_ptr; @@ -76,7 +80,7 @@ namespace Falcor \param[in] overrideSize If 0, will use the buffer size as declared in the shader. Otherwise, will use this value as the buffer size. Useful when using buffers with dynamic arrays. \return A new buffer object if the operation was successful, otherwise nullptr */ - static SharedPtr create(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t overrideSize = 0); + static SharedPtr create(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t overrideSize = 0, bool useDefaultHeap = false); /** Create a new constant buffer from a program object. Fetches the requested buffer reflector from the active program version and create the buffer from it \param[in] pProgram A program object which defines the buffer @@ -84,7 +88,7 @@ namespace Falcor \param[in] overrideSize If 0, will use the buffer size as declared in the shader. Otherwise, will use this value as the buffer size. Useful when using buffers with dynamic arrays. \return A new buffer object if the operation was successful, otherwise nullptr */ - static SharedPtr create(Program::SharedPtr& pProgram, const std::string& name, size_t overrideSize = 0); + static SharedPtr create(const Program* pProgram, const std::string& name, size_t overrideSize = 0, bool useDefaultHeap = false); ~ConstantBuffer(); @@ -94,7 +98,7 @@ namespace Falcor \param[in] value Value to set */ template - void setVariable(const std::string& name, const T& value) + bool setVariable(const std::string& name, const T& value) { return VariablesBuffer::setVariable(name, 0, value); } @@ -106,7 +110,7 @@ namespace Falcor \param[in] count pValue array size */ template - void setVariableArray(size_t offset, const T* pValue, size_t count) + bool setVariableArray(size_t offset, const T* pValue, size_t count) { return VariablesBuffer::setVariableArray(offset, 0, pValue, count); } @@ -117,7 +121,7 @@ namespace Falcor \param[in] value Value to set */ template - void setVariable(size_t offset, const T& value) + bool setVariable(size_t offset, const T& value) { return VariablesBuffer::setVariable(offset, 0, value); } @@ -129,7 +133,7 @@ namespace Falcor \param[in] count pValue array size */ template - void setVariableArray(const std::string& name, const T* pValue, size_t count) + bool setVariableArray(const std::string& name, const T* pValue, size_t count) { return VariablesBuffer::setVariableArray(name, 0, pValue, count); } @@ -138,7 +142,7 @@ namespace Falcor ConstantBufferView::SharedPtr getCbv(); protected: - ConstantBuffer(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t size); + ConstantBuffer(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t size, bool useDefaultHeap = false); mutable ConstantBufferView::SharedPtr mpCbv; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/StructuredBuffer.cpp b/Source/Falcor/Core/BufferTypes/StructuredBuffer.cpp similarity index 76% rename from Framework/Source/API/StructuredBuffer.cpp rename to Source/Falcor/Core/BufferTypes/StructuredBuffer.cpp index e80123f8e..c279581d6 100644 --- a/Framework/Source/API/StructuredBuffer.cpp +++ b/Source/Falcor/Core/BufferTypes/StructuredBuffer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/StructuredBuffer.h" -#include "API/Buffer.h" -#include +#include "stdafx.h" +#include "StructuredBuffer.h" +#include "Core/Program/Program.h" namespace Falcor { - template + template bool checkVariableByOffset(size_t offset, size_t count, const ReflectionResourceType* pReflection); - template + template bool checkVariableType(const ReflectionType* pShaderType, const std::string& name, const std::string& bufferName); -#define verify_element_index() if(elementIndex >= mElementCount) {logWarning(std::string(__FUNCTION__) + ": elementIndex is out-of-bound. Ignoring call."); return;} +#define verify_element_index() if(elementIndex >= mElementCount) {logError(std::string(__FUNCTION__) + ": elementIndex is out-of-bound. Ignoring call."); return false;} StructuredBuffer::StructuredBuffer(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflector, size_t elementCount, Resource::BindFlags bindFlags) : VariablesBuffer(name, pReflector, pReflector->getSize(), elementCount, bindFlags, Buffer::CpuAccess::None) @@ -56,16 +55,21 @@ namespace Falcor return pBuffer; } - StructuredBuffer::SharedPtr StructuredBuffer::create(const Program::SharedPtr& pProgram, const std::string& name, size_t elementCount, Resource::BindFlags bindFlags) + StructuredBuffer::SharedPtr StructuredBuffer::create(const Program* pProgram, const std::string& name, size_t elementCount, Resource::BindFlags bindFlags) { const auto& pProgReflector = pProgram->getReflector(); - const auto& pDefaultBlock = pProgReflector->getDefaultParameterBlock(); + return create(pProgram->getReflector().get(), name, elementCount, bindFlags); + } + + StructuredBuffer::SharedPtr StructuredBuffer::create(const ProgramReflection* pProgramReflection, const std::string& name, size_t elementCount, Resource::BindFlags bindFlags) + { + const auto& pDefaultBlock = pProgramReflection->getDefaultParameterBlock(); const ReflectionVar* pVar = pDefaultBlock ? pDefaultBlock->getResource(name).get() : nullptr; if (pVar) { ReflectionResourceType::SharedConstPtr pType = pVar->getType()->unwrapArray()->asResourceType()->inherit_shared_from_this::shared_from_this(); - if(pType && pType->getType() == ReflectionResourceType::Type::StructuredBuffer) + if (pType && pType->getType() == ReflectionResourceType::Type::StructuredBuffer) { return create(pVar->getName(), pType, elementCount, bindFlags); } @@ -74,25 +78,26 @@ namespace Falcor return nullptr; } - void StructuredBuffer::readFromGPU(size_t offset, size_t size) + bool StructuredBuffer::readFromGPU(size_t offset, size_t size) { - if(size == -1) + if (size == -1) { size = mSize - offset; } - if(size + offset > mSize) + if (size + offset > mSize) { - logWarning("StructuredBuffer::readFromGPU() - trying to read more data than what the buffer contains. Call is ignored."); - return; + logError("StructuredBuffer::readFromGPU() - trying to read more data than what the buffer contains. Call is ignored."); + return false; } - if(mGpuCopyDirty) + if (mGpuCopyDirty) { mGpuCopyDirty = false; const uint8_t* pData = (uint8_t*)map(Buffer::MapType::Read); std::memcpy(mData.data(), pData, mData.size()); unmap(); } + return true; } bool StructuredBuffer::hasUAVCounter() const @@ -103,30 +108,33 @@ namespace Falcor StructuredBuffer::~StructuredBuffer() = default; - void StructuredBuffer::readBlob(void* pDest, size_t offset, size_t size) - { - if(size + offset > mSize) + bool StructuredBuffer::readBlob(void* pDest, size_t offset, size_t size) + { + if (size + offset > mSize) { - logWarning("StructuredBuffer::readBlob() - trying to read more data than what the buffer contains. Call is ignored."); - return; + logError("StructuredBuffer::readBlob() - trying to read more data than what the buffer contains. Call is ignored."); + return false; } readFromGPU(); std::memcpy(pDest, mData.data() + offset, size); + return true; } - template - void StructuredBuffer::getVariable(size_t offset, size_t elementIndex, VarType& value) + template + bool StructuredBuffer::getVariable(size_t offset, size_t elementIndex, VarType& value) { verify_element_index(); - if(_LOG_ENABLED == 0 || checkVariableByOffset(offset, 0, mpReflector.get())) + if (_LOG_ENABLED == 0 || checkVariableByOffset(offset, 0, mpReflector.get())) { - readFromGPU(); + if (!readFromGPU()) return false; const uint8_t* pVar = mData.data() + offset + elementIndex * mElementSize; value = *(const VarType*)pVar; + return true; } + return false; } -#define get_constant_offset(_t) template void StructuredBuffer::getVariable(size_t offset, size_t elementIndex, _t& value) +#define get_constant_offset(_t) template dlldecl bool StructuredBuffer::getVariable(size_t offset, size_t elementIndex, _t& value) get_constant_offset(bool); get_constant_offset(glm::bvec2); @@ -165,17 +173,18 @@ namespace Falcor #undef get_constant_offset template - void StructuredBuffer::getVariable(const std::string& name, size_t elementIndex, VarType& value) + bool StructuredBuffer::getVariable(const std::string& name, size_t elementIndex, VarType& value) { - const auto& pVar = mpReflector->findMember(name); - + const auto& pVar = mpReflector->findMember(name); + if ((_LOG_ENABLED == 0) || (pVar && checkVariableType(pVar->getType().get(), name, mName))) { - getVariable(pVar->getOffset(), elementIndex, value); + return getVariable(pVar->getOffset(), elementIndex, value); } + return false; } -#define get_constant_string(_t) template void StructuredBuffer::getVariable(const std::string& name, size_t elementIndex, _t& value) +#define get_constant_string(_t) template dlldecl bool StructuredBuffer::getVariable(const std::string& name, size_t elementIndex, _t& value) get_constant_string(bool); get_constant_string(glm::bvec2); @@ -213,23 +222,25 @@ namespace Falcor #undef get_constant_string template - void StructuredBuffer::getVariableArray(size_t offset, size_t count, size_t elementIndex, VarType value[]) + bool StructuredBuffer::getVariableArray(size_t offset, size_t count, size_t elementIndex, VarType value[]) { verify_element_index(); if (_LOG_ENABLED == 0 || checkVariableByOffset(offset, count, mpReflector.get())) { - readFromGPU(); + if (!readFromGPU()) return false; const uint8_t* pVar = mData.data() + offset; const VarType* pMat = (VarType*)(pVar + elementIndex * mElementSize); for (size_t i = 0; i < count; i++) { value[i] = pMat[i]; } + return true; } + return false; } -#define get_constant_array_offset(_t) template void StructuredBuffer::getVariableArray(size_t offset, size_t count, size_t elementIndex, _t value[]) +#define get_constant_array_offset(_t) template dlldecl bool StructuredBuffer::getVariableArray(size_t offset, size_t count, size_t elementIndex, _t value[]) get_constant_array_offset(bool); get_constant_array_offset(glm::bvec2); @@ -268,16 +279,17 @@ namespace Falcor #undef get_constant_array_offset template - void StructuredBuffer::getVariableArray(const std::string& name, size_t count, size_t elementIndex, VarType value[]) + bool StructuredBuffer::getVariableArray(const std::string& name, size_t count, size_t elementIndex, VarType value[]) { const auto& pVar = mpReflector->findMember(name); if ((_LOG_ENABLED == 0) || (pVar && checkVariableType(pVar->getType().get(), name, mName))) { - getVariableArray(pVar->getOffset(), count, elementIndex, value); + return getVariableArray(pVar->getOffset(), count, elementIndex, value); } + return false; } -#define get_constant_array_string(_t) template void StructuredBuffer::getVariableArray(const std::string& name, size_t count, size_t elementIndex, _t value[]) +#define get_constant_array_string(_t) template dlldecl bool StructuredBuffer::getVariableArray(const std::string& name, size_t count, size_t elementIndex, _t value[]) get_constant_array_string(bool); get_constant_array_string(glm::bvec2); diff --git a/Framework/Source/API/StructuredBuffer.h b/Source/Falcor/Core/BufferTypes/StructuredBuffer.h similarity index 86% rename from Framework/Source/API/StructuredBuffer.h rename to Source/Falcor/Core/BufferTypes/StructuredBuffer.h index 4d0121422..c99444d3d 100644 --- a/Framework/Source/API/StructuredBuffer.h +++ b/Source/Falcor/Core/BufferTypes/StructuredBuffer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,23 +26,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include -#include "VariablesBuffer.h" +#include "Core/BufferTypes/VariablesBuffer.h" namespace Falcor { - class Buffer; - class ProgramVersion; - class Texture; - class Sampler; + class Program; /** Manages an array-of-structs style shader buffer, known as Structured Buffers in DirectX. Even though the buffer is created with a specific reflection object, it can be used with other programs as long as the buffer declarations are the same across programs. */ - class StructuredBuffer : public VariablesBuffer, public inherit_shared_from_this + class dlldecl StructuredBuffer : public VariablesBuffer, public inherit_shared_from_this { public: + using inherit_shared_from_this::shared_from_this; + class SharedPtr : public std::shared_ptr { public: @@ -87,14 +84,15 @@ namespace Falcor \return A new buffer object if the operation was successful, otherwise nullptr */ static SharedPtr create(const std::string& name, const ReflectionResourceType::SharedConstPtr& pReflectionType, size_t elementCount = 1, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); - + /** Create a structured buffer. Fetches the requested buffer reflector from the active program version and create the buffer from it \param[in] pProgram A program object which defines the buffer \param[in] elementCount The number of struct elements in the buffer \param[in] bindFlags The bind flags for the resource \return A new buffer object if the operation was successful, otherwise nullptr */ - static SharedPtr create(const Program::SharedPtr& pProgram, const std::string& name, size_t elementCount = 1, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + static SharedPtr create(const Program* pProgram, const std::string& name, size_t elementCount = 1, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + static SharedPtr create(const ProgramReflection* pProgramReflection, const std::string& name, size_t elementCount = 1, Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); ~StructuredBuffer(); @@ -104,7 +102,7 @@ namespace Falcor \param[out] value The value read from the buffer */ template - void getVariable(const std::string& name, size_t elementIndex, T& value); + bool getVariable(const std::string& name, size_t elementIndex, T& value); /** Read an array of variables from the buffer. The function will validate that the value Type matches the declaration in the shader. If there's a mismatch, an error will be logged and the call will be ignored. @@ -113,7 +111,7 @@ namespace Falcor \param[out] value Pointer to an array of values to read into */ template - void getVariableArray(size_t offset, size_t count, size_t elementIndex, T value[]); + bool getVariableArray(size_t offset, size_t count, size_t elementIndex, T value[]); /** Read a variable from the buffer. The function will validate that the value Type matches the declaration in the shader. If there's a mismatch, an error will be logged and the call will be ignored. @@ -121,7 +119,7 @@ namespace Falcor \param[out] value The value read from the buffer */ template - void getVariable(size_t offset, size_t elementIndex, T& value); + bool getVariable(size_t offset, size_t elementIndex, T& value); /** Read an array of variables from the buffer. The function will validate that the value Type matches the declaration in the shader. If there's a mismatch, an error will be logged and the call will be ignored. @@ -130,22 +128,23 @@ namespace Falcor \param[out] value Pointer to an array of values to read into */ template - void getVariableArray(const std::string& name, size_t count, size_t elementIndex, T value[]); + bool getVariableArray(const std::string& name, size_t count, size_t elementIndex, T value[]); /** Read a block of data from the buffer. If Offset + Size will result in buffer overflow, the call will be ignored and log an error. \param pDst Pointer to a buffer to write the data into \param offset Byte offset to start reading from the buffer \param size Number of bytes to read from the buffer + \return true if the read succedded, otherwise false */ - void readBlob(void* pDst, size_t offset, size_t size); + bool readBlob(void* pDst, size_t offset, size_t size); /** Read the buffer data from the GPU.\n Note that it is possible to use this function to update only part of the CPU copy of the buffer. This might lead to inconsistencies between the GPU and CPU buffer, so make sure you know what you are doing. \param[in] offset Offset into the buffer to read from \param[in] size Number of bytes to read. If this value is -1, will update the [Offset, EndOfBuffer] range. */ - void readFromGPU(size_t offset = 0, size_t size = -1); + bool readFromGPU(size_t offset = 0, size_t size = -1); /** Set the GPUCopyDirty flag */ @@ -165,4 +164,4 @@ namespace Falcor Buffer::SharedPtr mpUAVCounter; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/TypedBuffer.cpp b/Source/Falcor/Core/BufferTypes/TypedBuffer.cpp similarity index 76% rename from Framework/Source/API/TypedBuffer.cpp rename to Source/Falcor/Core/BufferTypes/TypedBuffer.cpp index 9ca5d0f36..d9069fb5f 100644 --- a/Framework/Source/API/TypedBuffer.cpp +++ b/Source/Falcor/Core/BufferTypes/TypedBuffer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "TypedBuffer.h" -#include namespace Falcor { @@ -41,11 +40,8 @@ namespace Falcor bool TypedBufferBase::uploadToGPU() { - if (mCpuDirty == false) - { - return false; - } - updateData(mData.data(), 0, mSize); + if (mCpuDirty == false) return false; + Buffer::setBlob(mData.data(), 0, mSize); mCpuDirty = false; return true; } @@ -60,4 +56,24 @@ namespace Falcor mGpuDirty = false; } } -} \ No newline at end of file + + bool TypedBufferBase::setBlob(const void* pSrc, size_t offset, size_t size) + { + if ((_LOG_ENABLED != 0) && (offset + size > mSize)) + { + std::string Msg("Error when setting blob to buffer\""); + Msg += mName + "\". Blob to large and will result in overflow. Ignoring call."; + logError(Msg); + return false; + } + std::memcpy(mData.data() + offset, pSrc, size); + mCpuDirty = true; + return true; + } + + void* TypedBufferBase::getData() + { + readFromGpu(); + return mData.data(); + } +} diff --git a/Framework/Source/API/TypedBuffer.h b/Source/Falcor/Core/BufferTypes/TypedBuffer.h similarity index 94% rename from Framework/Source/API/TypedBuffer.h rename to Source/Falcor/Core/BufferTypes/TypedBuffer.h index aef1a89a7..b7cec8259 100644 --- a/Framework/Source/API/TypedBuffer.h +++ b/Source/Falcor/Core/BufferTypes/TypedBuffer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,15 +26,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Buffer.h" -#include "ResourceViews.h" -#include +#include "Core/API/Buffer.h" namespace Falcor { /** Manages a shader buffer containing a simple array of data. */ - class TypedBufferBase : public Buffer + class dlldecl TypedBufferBase : public Buffer { public: using SharedPtr = std::shared_ptr; @@ -54,7 +52,16 @@ namespace Falcor /** Get the resource format associated with this buffer */ ResourceFormat getResourceFormat() const { return mFormat; } + + /** Set a blob + */ + virtual bool setBlob(const void* pData, size_t offset, size_t size) override; + + /** Get the data + */ + void* getData(); protected: + TypedBufferBase(uint32_t elementCount, ResourceFormat format, Resource::BindFlags bindFlags); void readFromGpu(); uint32_t mElementCount = 0; diff --git a/Framework/Source/API/VariablesBuffer.cpp b/Source/Falcor/Core/BufferTypes/VariablesBuffer.cpp similarity index 83% rename from Framework/Source/API/VariablesBuffer.cpp rename to Source/Falcor/Core/BufferTypes/VariablesBuffer.cpp index 018b482ed..c67e617cd 100644 --- a/Framework/Source/API/VariablesBuffer.cpp +++ b/Source/Falcor/Core/BufferTypes/VariablesBuffer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,15 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "VariablesBuffer.h" -#include "Graphics/Program/ProgramVersion.h" -#include "Buffer.h" -#include "Texture.h" -#include "Graphics/Program/ProgramReflection.h" -#include "API/Device.h" -#include "Utils/VariablesBufferUI.h" -#include +#include "Core/Program/ProgramReflection.h" +#include "VariablesBufferUI.h" namespace Falcor { @@ -95,23 +90,15 @@ namespace Falcor bool VariablesBuffer::uploadToGPU(size_t offset, size_t size) { - if(mDirty == false) - { - return false; - } - - if(size == -1) - { - size = mSize - offset; - } - + if(mDirty == false) return false; + if(size == -1) size = mSize - offset; if(size + offset > mSize) { - logWarning("VariablesBuffer::uploadToGPU() - trying to upload more data than what the buffer contains. Call is ignored."); + logError("VariablesBuffer::uploadToGPU() - trying to upload more data than what the buffer contains. Call is ignored."); return false; } - updateData(mData.data(), offset, size); + Buffer::setBlob(mData.data(), offset, size); mDirty = false; return true; } @@ -167,10 +154,10 @@ namespace Falcor #endif } -#define verify_element_index() if(elementIndex >= mElementCount) {logWarning(std::string(__FUNCTION__) + ": elementIndex is out-of-bound. Ignoring call."); return;} +#define verify_element_index() if(elementIndex >= mElementCount) {logError(std::string(__FUNCTION__) + ": elementIndex is out-of-bound. Ignoring call."); return false;} template - void VariablesBuffer::setVariable(size_t offset, size_t elementIndex, const VarType& value) + bool VariablesBuffer::setVariable(size_t offset, size_t elementIndex, const VarType& value) { verify_element_index(); if(checkVariableByOffset(offset, 0, mpReflector.get())) @@ -178,10 +165,12 @@ namespace Falcor const uint8_t* pVar = mData.data() + offset + elementIndex * mElementSize; *(VarType*)pVar = value; mDirty = true; + return true; } + return false; } -#define set_constant_by_offset(_t) template void VariablesBuffer::setVariable(size_t offset, size_t elementIndex, const _t& value) +#define set_constant_by_offset(_t) template dlldecl bool VariablesBuffer::setVariable(size_t offset, size_t elementIndex, const _t& value) set_constant_by_offset(bool); set_constant_by_offset(glm::bvec2); set_constant_by_offset(glm::bvec3); @@ -219,16 +208,17 @@ namespace Falcor #undef set_constant_by_offset template - void VariablesBuffer::setVariable(const std::string& name, size_t element, const VarType& value) + bool VariablesBuffer::setVariable(const std::string& name, size_t element, const VarType& value) { const auto& pVar = mpReflector->findMember(name); if((_LOG_ENABLED == 0) || (pVar && checkVariableType(pVar->getType().get(), name, mName))) { - setVariable(pVar->getOffset(), element, value); + return setVariable(pVar->getOffset(), element, value); } + return false; } -#define set_constant_by_name(_t) template void VariablesBuffer::setVariable(const std::string& name, size_t element, const _t& value) +#define set_constant_by_name(_t) template dlldecl bool VariablesBuffer::setVariable(const std::string& name, size_t element, const _t& value) set_constant_by_name(bool); set_constant_by_name(glm::bvec2); @@ -266,7 +256,7 @@ namespace Falcor #undef set_constant_by_name template - void VariablesBuffer::setVariableArray(size_t offset, size_t elementIndex, const VarType* pValue, size_t count) + bool VariablesBuffer::setVariableArray(size_t offset, size_t elementIndex, const VarType* pValue, size_t count) { verify_element_index(); if(checkVariableByOffset(offset, count, mpReflector.get())) @@ -278,10 +268,12 @@ namespace Falcor pData[i] = pValue[i]; } mDirty = true; + return true; } + return false; } -#define set_constant_array_by_offset(_t) template void VariablesBuffer::setVariableArray(size_t offset, size_t elementIndex, const _t* pValue, size_t count) +#define set_constant_array_by_offset(_t) template dlldecl bool VariablesBuffer::setVariableArray(size_t offset, size_t elementIndex, const _t* pValue, size_t count) set_constant_array_by_offset(bool); set_constant_array_by_offset(glm::bvec2); @@ -320,7 +312,7 @@ namespace Falcor #undef set_constant_array_by_offset template - void VariablesBuffer::setVariableArray(const std::string& name, size_t elementIndex, const VarType* pValue, size_t count) + bool VariablesBuffer::setVariableArray(const std::string& name, size_t elementIndex, const VarType* pValue, size_t count) { const auto& pVar = mpReflector->findMember(name); if( _LOG_ENABLED == 0 || (pVar && checkVariableType(pVar->getType().get(), name, mName))) @@ -328,22 +320,24 @@ namespace Falcor #if _LOG_ENABLED if (pVar->getType()->asArrayType() == nullptr) { - logWarning("Can't use VariablesBuffer::setVariableArray() on " + name + ". It is not an array."); - return; + logError("Can't use VariablesBuffer::setVariableArray() on " + name + ". It is not an array."); + return false; } // #PARAMBLOCK // if (count - elementIndex > pVar->getType()->getArraySize()) // { -// logWarning("VariablesBuffer::setVariableArray() - setting to many elements. Clamping..."); +// logError("VariablesBuffer::setVariableArray() - setting to many elements. Clamping..."); // count = pVar->getType()->getArraySize() - elementIndex; +// return false; // } #endif - setVariableArray(pVar->getOffset(), elementIndex, pValue, count); + return setVariableArray(pVar->getOffset(), elementIndex, pValue, count); } + return false; } -#define set_constant_array_by_string(_t) template void VariablesBuffer::setVariableArray(const std::string& name, size_t elementIndex, const _t* pValue, size_t count) +#define set_constant_array_by_string(_t) template dlldecl bool VariablesBuffer::setVariableArray(const std::string& name, size_t elementIndex, const _t* pValue, size_t count) set_constant_array_by_string(bool); set_constant_array_by_string(glm::bvec2); @@ -381,17 +375,19 @@ namespace Falcor #undef set_constant_array_by_string - void VariablesBuffer::setBlob(const void* pSrc, size_t offset, size_t size) + bool VariablesBuffer::setBlob(const void* pSrc, size_t offset, size_t size) { - if((_LOG_ENABLED != 0) && (offset + size > mSize)) + if((_LOG_ENABLED != 0) && (offset == kInvalidOffset || (offset + size > mSize))) { - std::string Msg("Error when setting blob to buffer\""); - Msg += mName + "\". Blob to large and will result in overflow. Ignoring call."; + std::string Msg("Error when setting blob to buffer \""); + if (offset == kInvalidOffset) Msg += mName + "\". Variable doesn't exist. Ignoring call."; + else Msg += mName + "\". Blob to large and will result in overflow. Ignoring call."; logError(Msg); - return; + return false; } std::memcpy(mData.data() + offset, pSrc, size); mDirty = true; + return true; } void VariablesBuffer::renderUI(Gui* pGui, const char* uiGroup) diff --git a/Framework/Source/API/VariablesBuffer.h b/Source/Falcor/Core/BufferTypes/VariablesBuffer.h similarity index 84% rename from Framework/Source/API/VariablesBuffer.h rename to Source/Falcor/Core/BufferTypes/VariablesBuffer.h index dea468a69..8c922a210 100644 --- a/Framework/Source/API/VariablesBuffer.h +++ b/Source/Falcor/Core/BufferTypes/VariablesBuffer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,16 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include "Graphics/Program/ProgramReflection.h" -#include "Texture.h" -#include "Buffer.h" -#include "Graphics/Program//Program.h" +#include "Core/API/Buffer.h" +#include "Core/Program/ProgramReflection.h" namespace Falcor { - class Sampler; - // Forward declares for gui draw func class Gui; /** Manages shader buffers containing named data, such as Constant/Uniform Buffers and Structured Buffers. @@ -43,11 +38,12 @@ namespace Falcor Note that Falcor has 2 flavors of setting variable by names - SetVariable() and SetVariableArray(). Naming rules for N-dimensional arrays of a basic Type are a little different between the two. SetVariable() must include N indices. SetVariableArray() can include N indices, or N-1 indices (implicit [0] as last index). */ - class VariablesBuffer : public Buffer, public inherit_shared_from_this + class dlldecl VariablesBuffer : public Buffer, public inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; static const size_t kInvalidOffset = -1;// ProgramReflection::kInvalidLocation; @@ -64,17 +60,19 @@ namespace Falcor /** Get the reflection object describing the CB */ - ReflectionType::SharedConstPtr getBufferReflector() const { return mpReflector; } + const ReflectionResourceType::SharedConstPtr& getBufferReflector() const { return mpReflector; } /** Set a block of data into the constant buffer.\n If Offset + Size will result in buffer overflow, the call will be ignored and log an error. \param[in] pSrc Pointer to the source data. \param[in] offset Destination offset inside the buffer. \param[in] size Number of bytes in the source data. + \return false in case of an error, otherwise true */ - void setBlob(const void* pSrc, size_t offset, size_t size); + bool setBlob(const void* pSrc, size_t offset, size_t size) override; /** Get a variable offset inside the buffer. See notes about naming in the VariablesBuffer class description. Constant name can be provided with an implicit array-index, similar to VariablesBuffer#SetVariableArray. + \return Offset or kInvalidOffset if the variable doesn't exist. */ size_t getVariableOffset(const std::string& varName) const; @@ -93,16 +91,16 @@ namespace Falcor protected: template - void setVariable(const std::string& name, size_t elementIndex, const T& value); + bool setVariable(const std::string& name, size_t elementIndex, const T& value); template - void setVariableArray(size_t offset, size_t elementIndex, const T* pValue, size_t count); + bool setVariableArray(size_t offset, size_t elementIndex, const T* pValue, size_t count); template - void setVariable(size_t offset, size_t elementIndex, const T& value); + bool setVariable(size_t offset, size_t elementIndex, const T& value); template - void setVariableArray(const std::string& name, size_t elementIndex, const T* pValue, size_t count); + bool setVariableArray(const std::string& name, size_t elementIndex, const T* pValue, size_t count); ReflectionResourceType::SharedConstPtr mpReflector; std::vector mData; diff --git a/Framework/Source/Utils/VariablesBufferUI.cpp b/Source/Falcor/Core/BufferTypes/VariablesBufferUI.cpp similarity index 68% rename from Framework/Source/Utils/VariablesBufferUI.cpp rename to Source/Falcor/Core/BufferTypes/VariablesBufferUI.cpp index 107ede58a..d16963153 100644 --- a/Framework/Source/Utils/VariablesBufferUI.cpp +++ b/Source/Falcor/Core/BufferTypes/VariablesBufferUI.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,109 +25,109 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - +#include "stdafx.h" #include "VariablesBufferUI.h" -#include "Utils/Gui.h" +#include "Utils/UI/Gui.h" namespace Falcor { std::unordered_map VariablesBufferUI::mGuiArrayIndices; - bool renderGuiWidgetFromType(Gui* pGui, ReflectionBasicType::Type type, size_t offset, const std::string& name, std::vector& data) + bool renderGuiWidgetFromType(Gui::Widgets& widget, ReflectionBasicType::Type type, size_t offset, const std::string& name, std::vector& data) { bool returnValue = false; #define to_gui_widget(widgetName, baseType) \ - returnValue = pGui-> concat_strings(add, widgetName)(name.c_str(), *reinterpret_cast(data.data() + offset)); \ + returnValue = widget.widgetName(name.c_str(), *reinterpret_cast(data.data() + offset)); \ offset += sizeof(baseType); #define to_gui_widget_matrix(widgetName, baseType) \ - returnValue = pGui-> concat_strings(add, widgetName)(name.c_str(), *reinterpret_cast(data.data() + offset)); \ + returnValue = widget.widgetName(name.c_str(), *reinterpret_cast(data.data() + offset)); \ offset += sizeof(baseType); #define to_gui_widget_bvec(widgetName, baseType) \ { \ uint32_t* pUintData = reinterpret_cast(data.data() + offset); \ baseType tempBVec; \ for (int32_t i = 0; i < tempBVec.length(); ++i) { tempBVec[i] = pUintData[i]; } \ - returnValue = pGui->concat_strings(add, widgetName)(name.c_str(), tempBVec); \ + returnValue = widget.widgetName(name.c_str(), tempBVec); \ for (int32_t i = 0; i < tempBVec.length(); ++i) { pUintData[i] = tempBVec[i]; offset += sizeof(uint32_t); } \ } switch (type) { case ReflectionBasicType::Type::Bool4: - to_gui_widget_bvec(Bool4Var, glm::bvec4); + to_gui_widget_bvec(checkbox, glm::bvec4); break; case ReflectionBasicType::Type::Bool3: - to_gui_widget_bvec(Bool3Var, glm::bvec3); + to_gui_widget_bvec(checkbox, glm::bvec3); break; case ReflectionBasicType::Type::Bool2: - to_gui_widget_bvec(Bool2Var, glm::bvec2); + to_gui_widget_bvec(checkbox, glm::bvec2); break; case ReflectionBasicType::Type::Bool: - to_gui_widget(CheckBox, bool); + to_gui_widget(checkbox, bool); break; case ReflectionBasicType::Type::Uint4: case ReflectionBasicType::Type::Uint64_4: case ReflectionBasicType::Type::Int4: case ReflectionBasicType::Type::Int64_4: - to_gui_widget(Int4Var, glm::ivec4); + to_gui_widget(var, glm::ivec4); break; case ReflectionBasicType::Type::Uint3: case ReflectionBasicType::Type::Uint64_3: case ReflectionBasicType::Type::Int3: case ReflectionBasicType::Type::Int64_3: - to_gui_widget(Int3Var, glm::ivec3); + to_gui_widget(var, glm::ivec3); break; case ReflectionBasicType::Type::Uint2: case ReflectionBasicType::Type::Uint64_2: case ReflectionBasicType::Type::Int2: case ReflectionBasicType::Type::Int64_2: - to_gui_widget(Int2Var, glm::ivec2); + to_gui_widget(var, glm::ivec2); break; case ReflectionBasicType::Type::Uint: case ReflectionBasicType::Type::Uint64: case ReflectionBasicType::Type::Int: case ReflectionBasicType::Type::Int64: - to_gui_widget(IntVar, int); + to_gui_widget(var, int); break; case ReflectionBasicType::Type::Float: - to_gui_widget(FloatVar, float); + to_gui_widget(var, float); break; case ReflectionBasicType::Type::Float2: - to_gui_widget(Float2Var, glm::vec2); + to_gui_widget(var, glm::vec2); break; case ReflectionBasicType::Type::Float3: - to_gui_widget(Float3Var, glm::vec3); + to_gui_widget(var, glm::vec3); break; case ReflectionBasicType::Type::Float4: - to_gui_widget(Float4Var, glm::vec4); + to_gui_widget(var, glm::vec4); break; case ReflectionBasicType::Type::Float2x2: - to_gui_widget_matrix(MatrixVar, glm::mat2x2); + to_gui_widget_matrix(matrix, glm::mat2x2); break; case ReflectionBasicType::Type::Float2x3: - to_gui_widget_matrix(MatrixVar, glm::mat2x3); + to_gui_widget_matrix(matrix, glm::mat2x3); break; case ReflectionBasicType::Type::Float2x4: - to_gui_widget_matrix(MatrixVar, glm::mat2x4); + to_gui_widget_matrix(matrix, glm::mat2x4); break; case ReflectionBasicType::Type::Float3x2: - to_gui_widget_matrix(MatrixVar, glm::mat3x2); + to_gui_widget_matrix(matrix, glm::mat3x2); break; case ReflectionBasicType::Type::Float3x3: - to_gui_widget_matrix(MatrixVar, glm::mat3x3); + to_gui_widget_matrix(matrix, glm::mat3x3); break; case ReflectionBasicType::Type::Float3x4: - to_gui_widget_matrix(MatrixVar, glm::mat3x4); + to_gui_widget_matrix(matrix, glm::mat3x4); break; case ReflectionBasicType::Type::Float4x2: - to_gui_widget_matrix(MatrixVar, glm::mat4x2); + to_gui_widget_matrix(matrix, glm::mat4x2); break; case ReflectionBasicType::Type::Float4x3: - to_gui_widget_matrix(MatrixVar, glm::mat4x3); + to_gui_widget_matrix(matrix, glm::mat4x3); break; case ReflectionBasicType::Type::Float4x4: - to_gui_widget_matrix(MatrixVar, glm::mat4x4); + to_gui_widget_matrix(matrix, glm::mat4x4); break; case ReflectionBasicType::Type::Unknown: break; @@ -141,24 +141,24 @@ namespace Falcor return returnValue; } - void VariablesBufferUI::renderUIMemberInternal(Gui* pGui, const std::string& memberName, size_t memberOffset, size_t memberSize, const std::string& memberTypeString, const ReflectionBasicType::Type& memberType, size_t arraySize) + void VariablesBufferUI::renderUIMemberInternal(Gui::Widgets& widget, const std::string& memberName, size_t memberOffset, size_t memberSize, const std::string& memberTypeString, const ReflectionBasicType::Type& memberType, size_t arraySize) { // Display data from the stage memory - mVariablesBufferRef.mDirty |= renderGuiWidgetFromType(pGui, memberType, memberOffset, memberName, mVariablesBufferRef.mData); + mVariablesBufferRef.mDirty |= renderGuiWidgetFromType(widget, memberType, memberOffset, memberName, mVariablesBufferRef.mData); // Display name and then reflection data as tooltip std::string toolTipString = "Offset: " + std::to_string(memberOffset); toolTipString.append("\nSize: " + std::to_string(memberSize)); - if (arraySize > 1) + if (arraySize > 1) { toolTipString.append("\nArray Size: " + std::to_string(arraySize)); } toolTipString.append("\nType: " + memberTypeString); - pGui->addTooltip(toolTipString.c_str(), true); + widget.tooltip(toolTipString.c_str(), true); } - void VariablesBufferUI::renderUIVarInternal(Gui* pGui, const ReflectionVar::SharedConstPtr& pMember, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag) + void VariablesBufferUI::renderUIVarInternal(Gui::Widgets& widget, const ReflectionVar::SharedConstPtr& pMember, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag) { size_t numMembers = 1; size_t memberSize = 0; @@ -168,6 +168,7 @@ namespace Falcor const ReflectionArrayType* pArrayType = nullptr; bool baseTypeIsStruct = false; size_t currentOffset = startOffset + (pMember)->getOffset(); + std::shared_ptr arrayGroup; // First test is not basic type if (!pBasicType) @@ -176,17 +177,18 @@ namespace Falcor const ReflectionStructType* pStructType = (pMember)->getType()->asStructType(); if (pStructType) { - // Iterate through the internal struct - if (pGui->beginGroup(memberName)) + auto group = Gui::Group(widget, memberName); + if (group.open()) { - pGui->addSeparator(); + // Iterate through the internal struct + group.separator(); memberName.push_back('.'); - renderUIInternal(pGui, pStructType, memberName, currentOffset, dirtyFlag); + renderUIInternal(group, pStructType, memberName, currentOffset, dirtyFlag); memberName.pop_back(); - pGui->endGroup(); - pGui->addSeparator(); + group.separator(); + group.release(); } // skip to next member @@ -203,7 +205,8 @@ namespace Falcor memberSize = pArrayType->getArrayStride(); // only iterate through array if it is displaying - if (!pGui->beginGroup(memberName + "[" + std::to_string(numMembers) + "]")) + arrayGroup = std::shared_ptr(new Gui::Group(widget.gui(), memberName + "[" + std::to_string(numMembers) + "]")); + if (!arrayGroup->open()) { return; } @@ -231,45 +234,44 @@ namespace Falcor memberSize = pBasicType->getSize(); } - // Display member of the array std::string displayName = memberName; int32_t& memberIndex = mGuiArrayIndices[displayName]; - + if (numMembers > 1) { // display information for specific index of array std::string indexLabelString = (displayName + std::to_string(numMembers)); - pGui->addIntVar(("Array Index" + std::string("##") + indexLabelString).c_str(), memberIndex, 0, static_cast(numMembers - 1)); + arrayGroup->var(("Array Index" + std::string("##") + indexLabelString).c_str(), memberIndex, 0, static_cast(numMembers - 1)); currentOffset += (memberSize * memberIndex); displayName.append("[" + std::to_string(memberIndex) + ":" + std::to_string(numMembers) + "]"); } - if (baseTypeIsStruct) { - // For arrays of structs, display dropdown for struct before recursing through struct members - if (pGui->beginGroup(displayName)) + auto group = Gui::Group(widget, displayName); + if (group.open()) { + // For arrays of structs, display dropdown for struct before recursing through struct members displayName.push_back('.'); - renderUIInternal(pGui, pArrayType->getType()->asStructType(), displayName, currentOffset, dirtyFlag); - pGui->endGroup(); + renderUIInternal(group, pArrayType->getType()->asStructType(), displayName, currentOffset, dirtyFlag); + group.release(); } } else { // for basic types - renderUIMemberInternal(pGui, displayName, currentOffset, memberSize, to_string(memberType), memberType, numMembers); + renderUIMemberInternal(widget, displayName, currentOffset, memberSize, to_string(memberType), memberType, numMembers); } if (numMembers > 1) { - pGui->endGroup(); + arrayGroup->release(); } } - void VariablesBufferUI::renderUIInternal(Gui* pGui, const ReflectionType* pType, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag) + void VariablesBufferUI::renderUIInternal(Gui::Widgets& widget, const ReflectionType* pType, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag) { const ReflectionStructType* pStruct = pType->asStructType(); if (pStruct) @@ -280,11 +282,11 @@ namespace Falcor const ReflectionResourceType* pResourceType = pMember->getType()->asResourceType(); if (pResourceType && pResourceType->getStructuredBufferType() != ReflectionResourceType::StructuredType::Invalid) { - renderUIInternal(pGui, pMember->getType().get(), currentStructName + pMember->getName(), startOffset, dirtyFlag); + renderUIInternal(widget, pMember->getType().get(), currentStructName + pMember->getName(), startOffset, dirtyFlag); } else { - renderUIVarInternal(pGui, pMember, currentStructName, startOffset, dirtyFlag); + renderUIVarInternal(widget, pMember, currentStructName, startOffset, dirtyFlag); } } } @@ -298,7 +300,7 @@ namespace Falcor int32_t oldMemberIndex = memberIndex; size_t structSize = pType->getSize(); - if (pGui->addIntVar(("Structured Buffer Index" + std::string("##") + displayName).c_str(), memberIndex, 0)) + if (widget.var(("Structured Buffer Index" + std::string("##") + displayName).c_str(), memberIndex, 0)) { // since we can't get the actual number of structs by default, we test the validity of the offset size_t offset = (structSize * memberIndex); @@ -311,30 +313,34 @@ namespace Falcor startOffset += (structSize * memberIndex); displayName.append("[" + std::to_string(memberIndex) + "]"); - if (pGui->beginGroup(displayName.c_str())) + auto group = Gui::Group(widget, displayName.c_str()); + if (group.open()) { - renderUIInternal(pGui, pType->asResourceType()->getStructType().get(), displayName, startOffset, dirtyFlag); - pGui->endGroup(); + renderUIInternal(widget, pType->asResourceType()->getStructType().get(), displayName, startOffset, dirtyFlag); + group.release(); } } else { - renderUIInternal(pGui, pType->asResourceType()->getStructType().get(), currentStructName, startOffset, dirtyFlag); + renderUIInternal(widget, pType->asResourceType()->getStructType().get(), currentStructName, startOffset, dirtyFlag); } } } void VariablesBufferUI::renderUI(Gui* pGui, const char* uiGroup) { - if (!uiGroup || pGui->beginGroup(uiGroup)) - { + if (!uiGroup) uiGroup = "Variables Buffer"; + + auto group = Gui::Group(pGui, uiGroup); + if (group.open()) + { // begin recursion on first struct - renderUIInternal(pGui, mVariablesBufferRef.mpReflector.get(), "", 0, mVariablesBufferRef.mDirty); + renderUIInternal(group, mVariablesBufferRef.mpReflector.get(), "", 0, mVariablesBufferRef.mDirty); // dirty flag for uploading will be set by GUI mVariablesBufferRef.uploadToGPU(); - if(uiGroup != nullptr) pGui->endGroup(); + group.release(); } } } diff --git a/Framework/Source/Utils/VariablesBufferUI.h b/Source/Falcor/Core/BufferTypes/VariablesBufferUI.h similarity index 82% rename from Framework/Source/Utils/VariablesBufferUI.h rename to Source/Falcor/Core/BufferTypes/VariablesBufferUI.h index f39856615..980f289de 100644 --- a/Framework/Source/Utils/VariablesBufferUI.h +++ b/Source/Falcor/Core/BufferTypes/VariablesBufferUI.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,25 +25,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - #pragma once -#include "Graphics/Program/ProgramReflection.h" -#include "API/VariablesBuffer.h" +#include "Core/Program/ProgramReflection.h" namespace Falcor { // Forward declares for gui draw func class Gui; + class VariablesBuffer; - class VariablesBufferUI + class dlldecl VariablesBufferUI { public: VariablesBufferUI(VariablesBuffer& variablesBufferRef) : mVariablesBufferRef(variablesBufferRef) {} - void renderUI(Gui* pGui, const char* uiGroup); private: - VariablesBuffer& mVariablesBufferRef; static std::unordered_map mGuiArrayIndices; @@ -54,7 +51,7 @@ namespace Falcor \param[in] startOffset Starting offset in memory for nested structures \param[in] dirtyFlag If set then send data to gpu */ - void renderUIVarInternal(Gui* pGui, const ReflectionVar::SharedConstPtr& pMember, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag); + void renderUIVarInternal(Gui::Widgets& widget, const ReflectionVar::SharedConstPtr& pMember, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag); /** Recursive function for traversing reflection data and display ui \param[in] pGui Pointer to the gui structure for rendering @@ -63,7 +60,7 @@ namespace Falcor \param[in] startOffset Starting offset in memory for nested structures \param[in] dirtyFlag If set then send data to gpu */ - void renderUIInternal(Gui* pGui, const ReflectionType* pType, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag); + void renderUIInternal(Gui::Widgets& widget, const ReflectionType* pType, const std::string& currentStructName, size_t startOffset, bool& dirtyFlag); /** Render gui widget for reflected data \param[in] pGui Pointer to the gui structure for rendering @@ -72,6 +69,6 @@ namespace Falcor \param[in] memberSize size of the data in the member \param[in] memberType reflection type enum for the basic type */ - void renderUIMemberInternal(Gui* pGui, const std::string& memberName, size_t memberOffset, size_t memberSize, const std::string& memberTypeString, const ReflectionBasicType::Type& memberType, size_t arraySize = 0); + void renderUIMemberInternal(Gui::Widgets& widget, const std::string& memberName, size_t memberOffset, size_t memberSize, const std::string& memberTypeString, const ReflectionBasicType::Type& memberType, size_t arraySize = 0); }; } diff --git a/Framework/Source/FalcorConfig.h b/Source/Falcor/Core/FalcorConfig.h similarity index 85% rename from Framework/Source/FalcorConfig.h rename to Source/Falcor/Core/FalcorConfig.h index ea1fd9b71..d094944e2 100644 --- a/Framework/Source/FalcorConfig.h +++ b/Source/Falcor/Core/FalcorConfig.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,16 +27,10 @@ ***************************************************************************/ #pragma once -#ifdef _DEBUG -#define _LOG_ENABLED 1 -#else -#define _LOG_ENABLED 0 // Set this to 1 to enable log messages in release builds -#endif +#define _LOG_ENABLED 1 // Set this to 0 to disable logging and most error checks #define _PROFILING_ENABLED 1 // Set this to 1 to enable CPU/GPU profiling #define _PROFILING_LOG 0 // Set this to 1 to dump profiling data while profiler is active. #define _PROFILING_LOG_BATCH_SIZE 1024 * 1 // This can be used to control how many samples are accumulated before they are dumped to file. #define _ENABLE_NVAPI false // Controls NVIDIA specific DX extensions. If it is set to true, make sure you have the NVAPI package in your 'Externals' directory. View the readme for more information. - -#define FALCOR_USE_PYTHON 0 // Set to 1 to build Python embedding API and samples. See README.txt in "LearningWithEmbeddedPython" sample for more information. \ No newline at end of file diff --git a/Framework/FalcorSharedObjects/FalcorSharedObjects.cpp b/Source/Falcor/Core/Framework.cpp similarity index 75% rename from Framework/FalcorSharedObjects/FalcorSharedObjects.cpp rename to Source/Falcor/Core/Framework.cpp index 97e8f6cb9..ba6fbf088 100644 --- a/Framework/FalcorSharedObjects/FalcorSharedObjects.cpp +++ b/Source/Falcor/Core/Framework.cpp @@ -25,28 +25,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include -#include +#include "stdafx.h" namespace Falcor { - dlldecl Logger::Data gLoggerData; - - dlldecl void releaseSharedObjects() + SCRIPT_BINDING(ComparisonFunc) { -#ifndef FALCOR_VK - gBlitData = {}; -#endif - gpDrawCommandSig = nullptr; - gpDrawIndexCommandSig = nullptr; - gNullSrv = nullptr; - gNullCbv = nullptr; - gNullRtv = nullptr; - gNullUav = nullptr; - gNullDsv = nullptr; - gSamplerData = {}; - gRenderGraphs.clear(); - gFullScreenData = {}; - // Don't release gpDevice, the sample still needs it + auto comparison = m.enum_("Comparison"); + comparison.regEnumVal(ComparisonFunc::Disabled).regEnumVal(ComparisonFunc::LessEqual).regEnumVal(ComparisonFunc::GreaterEqual); + comparison.regEnumVal(ComparisonFunc::Less).regEnumVal(ComparisonFunc::Greater).regEnumVal(ComparisonFunc::Equal); + comparison.regEnumVal(ComparisonFunc::NotEqual).regEnumVal(ComparisonFunc::Always).regEnumVal(ComparisonFunc::Never); } -} \ No newline at end of file +} diff --git a/Framework/Source/Framework.h b/Source/Falcor/Core/Framework.h similarity index 80% rename from Framework/Source/Framework.h rename to Source/Falcor/Core/Framework.h index cfbc66caa..940f67969 100644 --- a/Framework/Source/Framework.h +++ b/Source/Falcor/Core/Framework.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,17 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "FalcorConfig.h" -#include -#include -#include -#include "Utils/Logger.h" -#include "Utils/Scripting/Scripting.h" - -#define GLM_FORCE_CTOR_INIT -#define GLM_ENABLE_EXPERIMENTAL -#include "glm/glm.hpp" -using namespace glm; +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING +#define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING // Define DLL export/import #ifdef _MSC_VER @@ -47,12 +38,22 @@ using namespace glm; #define falcorimport extern #endif // _MSC_VER -#ifdef BUILDING_SHARED_DLL +#ifdef FALCOR_DLL #define dlldecl falcorexport #else // BUILDING_SHARED_DLL #define dlldecl falcorimport #endif // BUILDING_SHARED_DLL +#include "Core/FalcorConfig.h" +#include +#include +#include +#include +#include +#include +#include "Utils/Logger.h" +#include "Utils/Math/Vector.h" + #ifndef arraysize #define arraysize(a) (sizeof(a)/sizeof(a[0])) #endif @@ -91,7 +92,6 @@ using namespace glm; #define safe_delete(_a) {delete _a; _a = nullptr;} #define safe_delete_array(_a) {delete[] _a; _a = nullptr;} -#define align_to(_alignment, _val) (((_val + _alignment - 1) / _alignment) * _alignment) #define stringize(a) #a #define concat_strings_(a, b) a##b #define concat_strings(a, b) concat_strings_(a, b) @@ -184,15 +184,32 @@ namespace Falcor return min(max(val, minVal), maxVal); } - /** Returns whether a number is a power of two + /** Returns whether an integer number is a power of two. */ template - inline bool isPowerOf2(T a) + inline typename std::enable_if::value, bool>::type isPowerOf2(T a) { - uint64_t t = (uint64_t)a; - return (t & (t - 1)) == 0; + return (a & (a - (T)1)) == 0; } + template + inline T div_round_up(T a, T b) { return (a + b - (T)1) / b; } + +#define align_to(_alignment, _val) ((((_val) + (_alignment) - 1) / (_alignment)) * (_alignment)) + + /** Helper class to check if a class has a vtable. + Usage: has_vtable::value is true if vtable exists, false otherwise. + */ + template + struct has_vtable + { + class derived : public T + { + virtual void force_the_vtable() {} + }; + enum { value = (sizeof(T) == sizeof(derived)) }; + }; + /*! @} */ @@ -219,9 +236,9 @@ namespace Falcor } #if defined(FALCOR_D3D12) -#include "API/D3D12/FalcorD3D12.h" +#include "Core/API/D3D12/FalcorD3D12.h" #elif defined(FALCOR_VK) -#include "API/Vulkan/FalcorVK.h" +#include "Core/API/Vulkan/FalcorVK.h" #else #error Undefined falcor backend. Make sure that a backend is selected in "FalcorConfig.h" #endif @@ -287,9 +304,40 @@ namespace Falcor } } #undef compare_str + + // Required to_string functions + using std::to_string; + inline std::string to_string(const std::string& s) { return '"' + s + '"'; } // Here for completeness + inline std::string to_string(bool b) { return b ? "true" : "false"; } + + template + std::string to_string(const std::pair& p) + { + return "[" + to_string(p.first) + ", " + to_string(p.second) + "]"; + } + + // Helper to check if a type has an iterator + template struct has_iterator : std::false_type {}; + template struct has_iterator> : std::true_type {}; + + template + std::enable_if_t::value, std::string> to_string(const T& t) + { + std::string s = "["; + bool first = true; + for (const auto i : t) + { + if (!first) s += ", "; + first = false; + s += to_string(i); + } + return s + "]"; + } } #if defined(_MSC_VER) +// Enable Windows visual styles +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #define deprecate(_ver_, _msg_) __declspec(deprecated("This function is deprecated and will be removed in Falcor " ## _ver_ ## ". " ## _msg_)) #define forceinline __forceinline using DllHandle = HMODULE; @@ -301,10 +349,11 @@ using DllHandle = void*; #define suppress_deprecation _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #endif -#include "Utils/Platform/OS.h" -#include "Utils/Profiler.h" +#include "Core/Platform/OS.h" +#include "Utils/Timing/Profiler.h" +#include "Utils/Scripting/Scripting.h" #if (_ENABLE_NVAPI == true) #include "nvapi.h" #pragma comment(lib, "nvapi64.lib") -#endif \ No newline at end of file +#endif diff --git a/Framework/Source/Utils/Platform/Linux/Linux.cpp b/Source/Falcor/Core/Platform/Linux/Linux.cpp similarity index 92% rename from Framework/Source/Utils/Platform/Linux/Linux.cpp rename to Source/Falcor/Core/Platform/Linux/Linux.cpp index 962f877e8..09394beb5 100644 --- a/Framework/Source/Utils/Platform/Linux/Linux.cpp +++ b/Source/Falcor/Core/Platform/Linux/Linux.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,23 +25,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Utils/StringUtils.h" -#include "Utils/Platform/OS.h" -#include "Utils/Logger.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "stdafx.h" +// #include "Utils/StringUtils.h" +// #include "Utils/Platform/OS.h" +// #include "Utils/Logger.h" +// +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include namespace fs = std::experimental::filesystem; namespace Falcor @@ -56,6 +55,9 @@ namespace Falcor MsgBoxButton msgBox(const std::string& msg, MsgBoxType mbType) { +#ifdef _TEST_ + throw std::exception(msg.c_str()); +#endif if (!gtk_init_check(0, nullptr)) { should_not_get_here(); @@ -74,6 +76,9 @@ namespace Falcor case MsgBoxType::AbortRetryIgnore: buttonType = GTK_BUTTONS_NONE; break; + case MsgBoxType::YesNo: + buttonType = GTK_BUTTONS_YES_NO; + break; default: should_not_get_here(); break; @@ -105,7 +110,7 @@ namespace Falcor } } - gtk_window_set_title(GTK_WINDOW(pDialog), "Falcor"); + gtk_window_set_title(GTK_WINDOW(pDialog), gMsgBoxTitle); gint result = gtk_dialog_run(GTK_DIALOG(pDialog)); gtk_widget_destroy(pDialog); gtk_widget_destroy(pParent); @@ -120,6 +125,10 @@ namespace Falcor return MsgBoxButton::Ok; case GTK_RESPONSE_CANCEL: return MsgBoxButton::Cancel; + case GTK_RESPONSE_YES: + return MsgBoxButton::Yes; + case GTK_RESPONSE_NO: + return MsgBoxButton::No; case gint(MsgResponseId::Retry): return MsgBoxButton::Retry; case gint(MsgResponseId::Abort): @@ -323,9 +332,15 @@ namespace Falcor return success; } - template bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename); - template bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename); + bool openFileDialog(const FileDialogFilterVec& filters, std::string& filename) + { + return fileDialogCommon(filters, filename); + } + bool saveFileDialog(const FileDialogFilterVec& filters, std::string& filename) + { + return fileDialogCommon(filters, filename); + } void setActiveWindowIcon(const std::string& iconFile) { // #TODO Not yet implemented @@ -513,4 +528,4 @@ namespace Falcor { return dlsym(dll, funcName.c_str()); } -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Platform/Linux/ProgressBarLinux.cpp b/Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp similarity index 96% rename from Framework/Source/Utils/Platform/Linux/ProgressBarLinux.cpp rename to Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp index 7f24cb83e..854cc6de7 100644 --- a/Framework/Source/Utils/Platform/Linux/ProgressBarLinux.cpp +++ b/Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Utils/Platform/ProgressBar.h" -#include -#include +#include "stdafx.h" +#include "Core/Platform/ProgressBar.h" +// #include +// #include namespace Falcor { diff --git a/Framework/Source/Utils/MonitorInfo.cpp b/Source/Falcor/Core/Platform/MonitorInfo.cpp similarity index 98% rename from Framework/Source/Utils/MonitorInfo.cpp rename to Source/Falcor/Core/Platform/MonitorInfo.cpp index b6a6c750f..40b614476 100644 --- a/Framework/Source/Utils/MonitorInfo.cpp +++ b/Source/Falcor/Core/Platform/MonitorInfo.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,15 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" #ifdef _WIN32 -#include "Framework.h" #include "MonitorInfo.h" - -#include #include #include -#include -#include "StringUtils.h" +#include "Utils/StringUtils.h" #pragma comment(lib, "setupapi.lib") diff --git a/Framework/Source/Utils/MonitorInfo.h b/Source/Falcor/Core/Platform/MonitorInfo.h similarity index 91% rename from Framework/Source/Utils/MonitorInfo.h rename to Source/Falcor/Core/Platform/MonitorInfo.h index 37f942b28..047887817 100644 --- a/Framework/Source/Utils/MonitorInfo.h +++ b/Source/Falcor/Core/Platform/MonitorInfo.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,19 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - // #TODO Implement MonitorInfo cross-platform. GLFW? #ifdef _WIN32 -#include -#include "glm/gtc/type_ptr.hpp" -#include "glm/gtx/polar_coordinates.hpp" -#include "glm/gtc/matrix_transform.hpp" namespace Falcor { /** A class to extract information about displays */ - class MonitorInfo + class dlldecl MonitorInfo { public: /** Description data structure @@ -63,4 +58,4 @@ namespace Falcor static void displayMonitorInfo(); }; } -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/Framework/Source/Utils/Platform/OS.cpp b/Source/Falcor/Core/Platform/OS.cpp similarity index 93% rename from Framework/Source/Utils/Platform/OS.cpp rename to Source/Falcor/Core/Platform/OS.cpp index f1f7c5720..9b1fa2f2a 100644 --- a/Framework/Source/Utils/Platform/OS.cpp +++ b/Source/Falcor/Core/Platform/OS.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,26 +25,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Utils/Platform/OS.h" +#include "stdafx.h" +#include #include "Utils/StringUtils.h" #include -#include + namespace fs = std::experimental::filesystem; namespace Falcor { - template - bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename); - - bool openFileDialog(const FileDialogFilterVec& filters, std::string& filename) - { - return fileDialogCommon(filters, filename); - } + std::string gMsgBoxTitle = "Falcor"; - bool saveFileDialog(const FileDialogFilterVec& filters, std::string& filename) + void msgBoxTitle(const std::string& title) { - return fileDialogCommon(filters, filename); + gMsgBoxTitle = title; } uint32_t getLowerPowerOf2(uint32_t a) @@ -59,6 +53,7 @@ namespace Falcor std::string(getWorkingDirectory()), std::string(getWorkingDirectory() + "/Data"), std::string(_PROJECT_DIR_) + "/ShadingUtils", + std::string(_PROJECT_DIR_) + "../Internal", std::string(getExecutableDirectory()), std::string(getExecutableDirectory() + "/Data"), diff --git a/Framework/Source/Utils/Platform/OS.h b/Source/Falcor/Core/Platform/OS.h similarity index 69% rename from Framework/Source/Utils/Platform/OS.h rename to Source/Falcor/Core/Platform/OS.h index 6ca41072d..71da557b2 100644 --- a/Framework/Source/Utils/Platform/OS.h +++ b/Source/Falcor/Core/Platform/OS.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,11 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include #include -#include -#include "API/Window.h" +#pragma warning (disable : 4251) namespace Falcor { @@ -41,20 +38,29 @@ namespace Falcor * @{ */ + /** Utility class to start/stop OS services + */ + class OSServices + { + public: + static void start(); + static void stop(); + }; + /** Adds an icon to the foreground window. \param[in] iconFile Icon file name \param[in] windowHandle The api handle of the window for which we need to set the icon to. nullptr will apply the icon to the foreground window */ - void setWindowIcon(const std::string& iconFile, Window::ApiHandle windowHandle); + dlldecl void setWindowIcon(const std::string& iconFile, WindowHandle windowHandle); /** Retrieves estimated/user-set pixel density of a display. \return integer value of number of pixels per inch. */ - int getDisplayDpi(); + dlldecl int getDisplayDpi(); /** Get the requested display scale factor */ - float getDisplayScaleFactor(); + dlldecl float getDisplayScaleFactor(); /** Type of message box to display */ @@ -64,6 +70,7 @@ namespace Falcor OkCancel, ///< OK/Cancel buttons RetryCancel, ///< Retry/Cancel buttons AbortRetryIgnore, ///< Abort/Retry/Ignore buttons + YesNo, ///< Yes/No buttons }; /** Types of buttons @@ -75,6 +82,8 @@ namespace Falcor Cancel, ///< Cancel Button Abort, ///< Abort Button Ignore, ///< Ignore Button + Yes, ///< Yes Button + No, ///< No Button }; /** Display a message box. By default, shows a message box with a single 'OK' button. @@ -82,23 +91,27 @@ namespace Falcor \param[in] mbType Optional. Type of message box to display \return An enum indicating which button was clicked */ - MsgBoxButton msgBox(const std::string& msg, MsgBoxType mbType = MsgBoxType::Ok); + dlldecl MsgBoxButton msgBox(const std::string& msg, MsgBoxType mbType = MsgBoxType::Ok); + + /** Set the title for message boxes. The default value is "Falcor" + */ + dlldecl void msgBoxTitle(const std::string& title); /** Finds a file in one of the media directories. The arguments must not alias. \param[in] filename The file to look for \param[in] fullPath If the file was found, the full path to the file. If the file wasn't found, this is invalid. \return true if the file was found, otherwise false */ - bool findFileInDataDirectories(const std::string& filename, std::string& fullPath); + dlldecl bool findFileInDataDirectories(const std::string& filename, std::string& fullPath); /** Given a filename, returns the shortest possible path to the file relative to the data directories. If the file is not relative to the data directories, return the original filename */ - std::string stripDataDirectories(const std::string& filename); + dlldecl std::string stripDataDirectories(const std::string& filename); /** Structure to help with file dialog file-extension filters */ - struct FileDialogFilter + struct dlldecl FileDialogFilter { FileDialogFilter(const std::string& ext_, const std::string& desc_ = {}) : ext(ext_), desc(desc_) {} std::string desc; // The description ("Portable Network Graphics") @@ -112,93 +125,99 @@ namespace Falcor \param[in] filename On successful return, the name of the file selected by the user. \return true if a file was selected, otherwise false (if the user clicked 'Cancel'). */ - bool openFileDialog(const FileDialogFilterVec& filters, std::string& filename); + dlldecl bool openFileDialog(const FileDialogFilterVec& filters, std::string& filename); /** Creates a 'save file' dialog box. \param[in] filters The file extensions filters - \param[in] filename On successful return, the name of the file selected by the user. + \param[out] filename On successful return, the name of the file selected by the user. \return true if a file was selected, otherwise false (if the user clicked 'Cancel'). */ - bool saveFileDialog(const FileDialogFilterVec& filters, std::string& filename); + dlldecl bool saveFileDialog(const FileDialogFilterVec& filters, std::string& filename); + + /** Creates a dialog box for browsing and selecting folders + \param[out] folder On successful return, the name of the folder selected by the user. + \return true if a folder was selected, otherwise false (if the user clicked 'Cancel'). + */ + dlldecl bool chooseFolderDialog(std::string& folder); /** Checks if a file exists in the file system. This function doesn't look in the common directories. \param[in] filename The file to look for \return true if the file was found, otherwise false */ - bool doesFileExist(const std::string& filename); + dlldecl bool doesFileExist(const std::string& filename); /** Checks if a directory exists in the file system. \param[in] filename The directory to look for \return true if the directory was found, otherwise false */ - bool isDirectoryExists(const std::string& filename); + dlldecl bool isDirectoryExists(const std::string& filename); /** Open watch thread for file changes and call callback when the file is written to. \param[in] full path to the file to watch for changes \param[in] callback function */ - void monitorFileUpdates(const std::string& filePath, const std::function& callback = {}); + dlldecl void monitorFileUpdates(const std::string& filePath, const std::function& callback = {}); /** Close watch thread for file changes \param[in] full path to the file that was being watched for changes */ - void closeSharedFile(const std::string& filePath); + dlldecl void closeSharedFile(const std::string& filePath); /** Creates a file in the temperary directory and returns the path. \return pathName Absolute path to unique temp file. */ - std::string getTempFilename(); + dlldecl std::string getTempFilename(); /** Create a directory from path. */ - bool createDirectory(const std::string& path); + dlldecl bool createDirectory(const std::string& path); /** Given the app name and full command line arguments, begin the process */ - size_t executeProcess(const std::string& appName, const std::string& commandLineArgs); + dlldecl size_t executeProcess(const std::string& appName, const std::string& commandLineArgs); /** Check if the given process is still active */ - bool isProcessRunning(size_t processID); + dlldecl bool isProcessRunning(size_t processID); /** Terminate process */ - void terminateProcess(size_t processID); + dlldecl void terminateProcess(size_t processID); /** Get the current executable directory \return The full path of the application directory */ - const std::string& getExecutableDirectory(); + dlldecl const std::string& getExecutableDirectory(); /** Get the current executable name \return The name of the executable */ - const std::string& getExecutableName(); + dlldecl const std::string& getExecutableName(); /** Get the working directory. This can be different from the executable directory (for example, by default when you launch an app from Visual Studio, the working the directory is the directory containing the project file). */ - const std::string getWorkingDirectory(); + dlldecl const std::string getWorkingDirectory(); /** Get the content of a system environment variable. \param[in] varName Name of the environment variable \param[out] value On success, will hold the value of the environment variable. \return true if environment variable was found, otherwise false. */ - bool getEnvironmentVariable(const std::string& varName, std::string& value); + dlldecl bool getEnvironmentVariable(const std::string& varName, std::string& value); /** Get a list of all recorded data directories. */ - const std::vector& getDataDirectoriesList(); + dlldecl const std::vector& getDataDirectoriesList(); /** Adds a folder into the search directory. Once added, calls to FindFileInCommonDirs() will seach that directory as well \param[in] dir The new directory to add to the common directories. */ - void addDataDirectory(const std::string& dir); + dlldecl void addDataDirectory(const std::string& dir); /** Removes a folder from the search directories \param[in] dir The directory name to remove from the common directories. */ - void removeDataDirectory(const std::string& dataDir); + dlldecl void removeDataDirectory(const std::string& dataDir); /** Find a new filename based on the supplied parameters. This function doesn't actually create the file, just find an available file name. \param[in] prefix Requested file prefix. @@ -207,43 +226,43 @@ namespace Falcor \param[out] filename On success, will hold a valid unused filename in the following format - 'Directory\\Prefix..Extension'. \return true if an available filename was found, otherwise false. */ - bool findAvailableFilename(const std::string& prefix, const std::string& directory, const std::string& extension, std::string& filename); + dlldecl bool findAvailableFilename(const std::string& prefix, const std::string& directory, const std::string& extension, std::string& filename); /** Check if a debugger session is attached. \return true if debugger is attached to the Falcor process. */ - bool isDebuggerPresent(); + dlldecl bool isDebuggerPresent(); /** Remove navigational elements ('.', '..) from a given path/filename and make slash direction consistent. */ - std::string canonicalizeFilename(const std::string& filename); + dlldecl std::string canonicalizeFilename(const std::string& filename); /** Breaks in debugger (int 3 functionality) */ - void debugBreak(); + dlldecl void debugBreak(); /** Print a message into the debug window \param[in] s Text to pring */ - void printToDebugWindow(const std::string& s); + dlldecl void printToDebugWindow(const std::string& s); /** Get directory from filename. \param[in] filename File path to strip directory from \return Stripped directory path */ - std::string getDirectoryFromFile(const std::string& filename); + dlldecl std::string getDirectoryFromFile(const std::string& filename); /** Get extension tag from filename. \param[in] filename File path to strip extension name from \return Stripped extension name. */ - std::string getExtensionFromFile(const std::string& filename); + dlldecl std::string getExtensionFromFile(const std::string& filename); /** Strip path from a full filename \param[in] filename File path \return Stripped filename */ - std::string getFilenameFromPath(const std::string& filename); + dlldecl std::string getFilenameFromPath(const std::string& filename); /** Swap file extension (very simple implementation) \param[in] str File name or full path @@ -251,27 +270,27 @@ namespace Falcor \param[in] newExtension Extension to replace the current with \return If end of str matches currentExtension, returns the file name replaced with the new extension, otherwise returns the original file name. */ - std::string swapFileExtension(const std::string& str, const std::string& currentExtension, const std::string& newExtension); + dlldecl std::string swapFileExtension(const std::string& str, const std::string& currentExtension, const std::string& newExtension); /** Enumerate files using search string \param[in] searchString String to use in file search \param[out] filenames Vector of found filenames */ - void enumerateFiles(std::string searchString, std::vector& filenames); + dlldecl void enumerateFiles(std::string searchString, std::vector& filenames); /** Return current thread handle */ - std::thread::native_handle_type getCurrentThread(); + dlldecl std::thread::native_handle_type getCurrentThread(); /** Sets thread affinity mask */ - void setThreadAffinity(std::thread::native_handle_type thread, uint32_t affinityMask); + dlldecl void setThreadAffinity(std::thread::native_handle_type thread, uint32_t affinityMask); /** Get the last time a file was modified. If the file is not found will return 0 \param[in] filename The file to look for \return Epoch timestamp of when the file was last modified */ - time_t getFileModifiedTime(const std::string& filename); + dlldecl time_t getFileModifiedTime(const std::string& filename); enum class ThreadPriorityType : int32_t { @@ -286,51 +305,54 @@ namespace Falcor /** Sets thread priority */ - void setThreadPriority(std::thread::native_handle_type thread, ThreadPriorityType priority); + dlldecl void setThreadPriority(std::thread::native_handle_type thread, ThreadPriorityType priority); /** Get the Total Virtual Memory. */ - uint64_t getTotalVirtualMemory(); + dlldecl uint64_t getTotalVirtualMemory(); /** Get the Used Virtual Memory. */ - uint64_t getUsedVirtualMemory(); + dlldecl uint64_t getUsedVirtualMemory(); /** Get the Virtual Memory Used by this Process. */ - uint64_t getProcessUsedVirtualMemory(); + dlldecl uint64_t getProcessUsedVirtualMemory(); /** Returns index of most significant set bit, or 0 if no bits were set. */ - uint32_t bitScanReverse(uint32_t a); + dlldecl uint32_t bitScanReverse(uint32_t a); /** Returns index of least significant set bit, or 0 if no bits were set. */ - uint32_t bitScanForward(uint32_t a); + dlldecl uint32_t bitScanForward(uint32_t a); /** Gets the closest power of two to a number, rounded down. */ - uint32_t getLowerPowerOf2(uint32_t a); + dlldecl uint32_t getLowerPowerOf2(uint32_t a); /** Gets the number of set bits. */ - uint32_t popcount(uint32_t a); + dlldecl uint32_t popcount(uint32_t a); /** Load the content of a file into a string */ - std::string readFile(const std::string& filename); + dlldecl std::string readFile(const std::string& filename); /** Load a shared-library */ - DllHandle loadDll(const std::string& libPath); + dlldecl DllHandle loadDll(const std::string& libPath); /** Release a shared-library */ - void releaseDll(DllHandle dll); + dlldecl void releaseDll(DllHandle dll); /** Get a function pointer from a library */ - void* getDllProcAddress(DllHandle dll, const std::string& funcName); + dlldecl void* getDllProcAddress(DllHandle dll, const std::string& funcName); + /** Post a quit message with an exit code + */ + dlldecl void postQuitMessage(int32_t exitCode); /*! @} */ -}; \ No newline at end of file +}; diff --git a/Source/Falcor/Core/Platform/ProgressBar.cpp b/Source/Falcor/Core/Platform/ProgressBar.cpp new file mode 100644 index 000000000..307aa481e --- /dev/null +++ b/Source/Falcor/Core/Platform/ProgressBar.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ProgressBar.h" + +namespace Falcor +{ + std::weak_ptr ProgressBar::spBar; + ProgressBarData* ProgressBar::spData = nullptr; + + ProgressBar::SharedPtr ProgressBar::show(const MessageList& list, uint32_t delayInMs) + { + SharedPtr pBar = spBar.lock(); + if (pBar) return pBar; + + pBar = SharedPtr(new ProgressBar()); + pBar->platformInit(list, delayInMs); + spBar = pBar; + return pBar; + } + + ProgressBar::SharedPtr ProgressBar::show(const char* pMsg, uint32_t delayInMs) + { + MessageList list; + if (pMsg) list.push_back(pMsg); + return show(list, delayInMs); + } + + ProgressBar::~ProgressBar() + { + close(); + } +} diff --git a/Framework/Source/Utils/Platform/ProgressBar.h b/Source/Falcor/Core/Platform/ProgressBar.h similarity index 84% rename from Framework/Source/Utils/Platform/ProgressBar.h rename to Source/Falcor/Core/Platform/ProgressBar.h index 67e842630..4e6dbfcb9 100644 --- a/Framework/Source/Utils/Platform/ProgressBar.h +++ b/Source/Falcor/Core/Platform/ProgressBar.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,9 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - #include -#include "API/Window.h" namespace Falcor { @@ -36,30 +34,32 @@ namespace Falcor /** Creates a progress bar visual and manages a new thread for it. */ - class ProgressBar + class dlldecl ProgressBar { public: using SharedPtr = std::shared_ptr; using MessageList = std::vector; + ~ProgressBar(); /** Creates a progress bar. \param[in] list List of messages to display on the progress bar \param[in] delayInMs Time between updates in milliseconds */ - static SharedPtr create(const MessageList& list, uint32_t delayInMs = 1000); + static SharedPtr show(const MessageList& list, uint32_t delayInMs = 1000); /** Creates a progress bar. \param[in] pMsg Message to display on the progress bar \param[in] delayInMs Time between updates in milliseconds */ - static SharedPtr create(const char* pMsg = nullptr, uint32_t delayInMs = 1000); - - ~ProgressBar(); + static SharedPtr show(const char* pMsg = nullptr, uint32_t delayInMs = 1000); + /** Close the progress bar + */ + static void close(); private: + static std::weak_ptr spBar; + static ProgressBarData* spData; ProgressBar() = default; - void platformInit(const MessageList& list, uint32_t delayInMs); - - ProgressBarData* mpData; + void platformInit(const MessageList& list, uint32_t delayInMs); }; -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Platform/Windows/ProgressBarWin.cpp b/Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp similarity index 87% rename from Framework/Source/Utils/Platform/Windows/ProgressBarWin.cpp rename to Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp index 4e929e634..5094edad1 100644 --- a/Framework/Source/Utils/Platform/Windows/ProgressBarWin.cpp +++ b/Source/Falcor/Core/Platform/Windows/ProgressBarWin.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Utils/Platform/ProgressBar.h" +#include "stdafx.h" +#include "Core/Platform/ProgressBar.h" #include #include -#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") - namespace Falcor { struct ProgressBarData @@ -45,12 +42,15 @@ namespace Falcor bool running = true; }; - ProgressBar::~ProgressBar() + void ProgressBar::close() { - mpData->running = false; - mpData->thread.join(); - DestroyWindow(mpData->hwnd); - safe_delete(mpData); + if(spData) + { + spData->running = false; + spData->thread.join(); + DestroyWindow(spData->hwnd); + safe_delete(spData); + } } void progressBarThread(ProgressBarData* pData, const ProgressBar::MessageList& msgList, uint32_t delayInMs) @@ -107,7 +107,7 @@ namespace Falcor void ProgressBar::platformInit(const MessageList& list, uint32_t delayInMs) { - mpData = new ProgressBarData; + spData = new ProgressBarData; // Initialize the common controls INITCOMMONCONTROLSEX init; @@ -116,7 +116,7 @@ namespace Falcor InitCommonControlsEx(&init); // Start the thread - mpData->thread = std::thread(progressBarThread, mpData, list, delayInMs); + spData->thread = std::thread(progressBarThread, spData, list, delayInMs); } } diff --git a/Framework/Source/Utils/Platform/Windows/Windows.cpp b/Source/Falcor/Core/Platform/Windows/Windows.cpp similarity index 83% rename from Framework/Source/Utils/Platform/Windows/Windows.cpp rename to Source/Falcor/Core/Platform/Windows/Windows.cpp index 691b3c64e..3a64c9540 100644 --- a/Framework/Source/Utils/Platform/Windows/Windows.cpp +++ b/Source/Falcor/Core/Platform/Windows/Windows.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,20 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Utils/Platform/OS.h" -#include -#include -#include "Utils/StringUtils.h" -#include -#include -#include -#include "API/Window.h" -#include "psapi.h" -#include "Utils/ThreadPool.h" -#include +#include "stdafx.h" #include +#include +#include +#include // Always run in Optimus mode on laptops extern "C" @@ -48,8 +39,13 @@ extern "C" namespace Falcor { + extern std::string gMsgBoxTitle; + MsgBoxButton msgBox(const std::string& msg, MsgBoxType mbType) { +#ifdef _TEST_ + throw std::exception(msg.c_str()); +#endif UINT Type = MB_OK; switch (mbType) { @@ -65,12 +61,15 @@ namespace Falcor case MsgBoxType::AbortRetryIgnore: Type = MB_ABORTRETRYIGNORE; break; + case MsgBoxType::YesNo: + Type = MB_YESNO; + break; default: should_not_get_here(); break; } - int value = MessageBoxA(nullptr, msg.c_str(), "Falcor", Type | MB_TOPMOST); + int value = MessageBoxA(nullptr, msg.c_str(), gMsgBoxTitle.c_str(), Type | MB_TOPMOST); switch (value) { case IDOK: @@ -83,6 +82,10 @@ namespace Falcor return MsgBoxButton::Abort; case IDIGNORE: return MsgBoxButton::Ignore; + case IDYES: + return MsgBoxButton::Yes; + case IDNO: + return MsgBoxButton::No; default: should_not_get_here(); return MsgBoxButton::Cancel; @@ -196,44 +199,85 @@ namespace Falcor return s; }; - template - bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename) + struct FilterSpec { - OPENFILENAMEA ofn; - CHAR chars[512] = ""; - ZeroMemory(&ofn, sizeof(ofn)); - - std::string filtersString = getExtensionsFilterString(filters); - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = GetForegroundWindow(); - ofn.lpstrFilter = filtersString.c_str(); - ofn.lpstrFile = chars; - ofn.nMaxFile = arraysize(chars); - ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR; - if (open == true) + FilterSpec(const FileDialogFilterVec& filters, bool forOpen) { - ofn.Flags |= OFN_FILEMUSTEXIST; + size_t size = forOpen ? filters.size() + 1 : filters.size(); + comDlg.reserve(size); + descs.reserve(size); + ext.reserve(size); + + if (forOpen) comDlg.push_back({}); + std::wstring all; + for(const auto& f : filters) + { + descs.push_back(string_2_wstring(f.desc)); + ext.push_back(L"*." + string_2_wstring(f.ext)); + comDlg.push_back({ descs.back().c_str(), ext.back().c_str() }); + all += ext.back() + L";"; + } + + if (forOpen) + { + descs.push_back(L"Supported Formats"); + ext.push_back(all); + comDlg[0] = { descs.back().c_str(), ext.back().c_str() }; + } } - ofn.lpstrDefExt = ""; - BOOL b = open ? GetOpenFileNameA(&ofn) : GetSaveFileNameA(&ofn); - if (b) + size_t size() const { return comDlg.size(); } + const COMDLG_FILTERSPEC* data() const { return comDlg.data(); } + private: + std::vector comDlg; + std::vector descs; + std::vector ext; + }; + + template + static bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename, DWORD options, const CLSID clsid) + { + FilterSpec fs(filters, typeid(DialogType) == typeid(IFileOpenDialog)); + + DialogType* pDialog; + d3d_call(CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pDialog))); + pDialog->SetOptions(options | FOS_FORCEFILESYSTEM); + pDialog->SetFileTypes((uint32_t)fs.size(), fs.data()); + + if (pDialog->Show(nullptr) == S_OK) { - filename = std::string(chars); - if (getExtensionFromFile(filename).empty() && filters.empty() == false) + IShellItem* pItem; + if (pDialog->GetResult(&pItem) == S_OK) { - filename += '.' + filters[ofn.nFilterIndex].ext; + PWSTR path; + if (pItem->GetDisplayName(SIGDN_FILESYSPATH, &path) == S_OK) + { + filename = wstring_2_string(std::wstring(path)); + CoTaskMemFree(path); + return true; + } } - return true; } + return false; } - template bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename); - template bool fileDialogCommon(const FileDialogFilterVec& filters, std::string& filename); + bool saveFileDialog(const FileDialogFilterVec& filters, std::string& filename) + { + return fileDialogCommon(filters, filename, FOS_OVERWRITEPROMPT, CLSID_FileSaveDialog); + } + + bool openFileDialog(const FileDialogFilterVec& filters, std::string& filename) + { + return fileDialogCommon(filters, filename, FOS_FILEMUSTEXIST, CLSID_FileOpenDialog); + }; + + bool chooseFolderDialog(std::string& folder) + { + return fileDialogCommon({}, folder, FOS_PICKFOLDERS | FOS_PATHMUSTEXIST, CLSID_FileOpenDialog); + } - void setWindowIcon(const std::string& iconFile, Window::ApiHandle windowHandle) + void setWindowIcon(const std::string& iconFile, WindowHandle windowHandle) { std::string fullpath; if (findFileInDataDirectories(iconFile, fullpath)) @@ -589,4 +633,19 @@ namespace Falcor { return GetProcAddress(dll, funcName.c_str()); } + + void postQuitMessage(int32_t exitCode) + { + PostQuitMessage(exitCode); + } + + void OSServices::start() + { + d3d_call(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); + } + + void OSServices::stop() + { + CoUninitialize(); + } } diff --git a/Framework/Source/Graphics/Program/ComputeProgram.cpp b/Source/Falcor/Core/Program/ComputeProgram.cpp similarity index 84% rename from Framework/Source/Graphics/Program/ComputeProgram.cpp rename to Source/Falcor/Core/Program/ComputeProgram.cpp index cbeba1799..59c69d553 100644 --- a/Framework/Source/Graphics/Program/ComputeProgram.cpp +++ b/Source/Falcor/Core/Program/ComputeProgram.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,29 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ComputeProgram.h" namespace Falcor { ComputeProgram::SharedPtr ComputeProgram::createFromFile(const std::string& filename, const std::string& csEntry, const DefineList& programDefines, Shader::CompilerFlags flags, const std::string& shaderModel) { - SharedPtr pProg = SharedPtr(new ComputeProgram); Desc d(filename); if (!shaderModel.empty()) d.setShaderModel(shaderModel); d.setCompilerFlags(flags); d.csEntry(csEntry); - pProg->init(d, programDefines); + return create(d, programDefines); + } + + ComputeProgram::SharedPtr ComputeProgram::create(const Program::Desc& desc, const DefineList& programDefines) + { + SharedPtr pProg = SharedPtr(new ComputeProgram); + pProg->init(desc, programDefines); return pProg; } -} \ No newline at end of file + + SCRIPT_BINDING(ComputeProgram) + { + m.regClass(ComputeProgram); + } +} diff --git a/Framework/Source/Graphics/Program/ComputeProgram.h b/Source/Falcor/Core/Program/ComputeProgram.h similarity index 82% rename from Framework/Source/Graphics/Program/ComputeProgram.h rename to Source/Falcor/Core/Program/ComputeProgram.h index 3d95e3ce1..2b1729b6f 100644 --- a/Framework/Source/Graphics/Program/ComputeProgram.h +++ b/Source/Falcor/Core/Program/ComputeProgram.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,12 +32,13 @@ namespace Falcor { /** Compute program. */ - class ComputeProgram : public Program, inherit_shared_from_this + class dlldecl ComputeProgram : public Program, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; ~ComputeProgram() = default; + using inherit_shared_from_this::shared_from_this; /** Create a new program object. \param[in] filename Compute shader filename. Can also include a full path or relative path from a data directory. @@ -47,7 +48,13 @@ namespace Falcor Note that this call merely creates a program object. The actual compilation and link happens when calling Program#getActiveVersion(). */ static SharedPtr createFromFile(const std::string& filename, const std::string& csEntry, const DefineList& programDefines = DefineList(), Shader::CompilerFlags flags = Shader::CompilerFlags::None, const std::string& shaderModel = ""); + + /** Create a new object + \param[in] desc The program's description + \param[in] defines Optional. A list of macro definitions to be patched into the shaders. + */ + static SharedPtr create(const Program::Desc& desc, const DefineList& programDefines = DefineList()); private: ComputeProgram() = default; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/GraphicsProgram.cpp b/Source/Falcor/Core/Program/GraphicsProgram.cpp similarity index 93% rename from Framework/Source/Graphics/Program/GraphicsProgram.cpp rename to Source/Falcor/Core/Program/GraphicsProgram.cpp index fa252c05d..1937e9514 100644 --- a/Framework/Source/Graphics/Program/GraphicsProgram.cpp +++ b/Source/Falcor/Core/Program/GraphicsProgram.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "GraphicsProgram.h" namespace Falcor @@ -46,4 +46,9 @@ namespace Falcor d.vsEntry(vsEntry).psEntry(psEntry).addDefaultVertexShaderIfNeeded(); return create(d, programDefines); } -} \ No newline at end of file + + SCRIPT_BINDING(GraphicsProgram) + { + m.regClass(GraphicsProgram); + } +} diff --git a/Framework/Source/Graphics/Program/GraphicsProgram.h b/Source/Falcor/Core/Program/GraphicsProgram.h similarity index 86% rename from Framework/Source/Graphics/Program/GraphicsProgram.h rename to Source/Falcor/Core/Program/GraphicsProgram.h index 75b3ea379..64bf57791 100644 --- a/Framework/Source/Graphics/Program/GraphicsProgram.h +++ b/Source/Falcor/Core/Program/GraphicsProgram.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,11 +32,12 @@ namespace Falcor { /** Graphics program. See ComputeProgram to manage compute shaders. */ - class GraphicsProgram : public Program, inherit_shared_from_this + class dlldecl GraphicsProgram : public Program, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; ~GraphicsProgram() = default; @@ -48,17 +49,16 @@ namespace Falcor */ static SharedPtr create(const Desc& desc, const Program::DefineList& programDefines = DefineList()); - /** Create a new program object. - \param[in] vertexFile Vertex shader filename. If this string is empty (""), it will use a default vertex shader which transforms and outputs all default vertex attributes. - \param[in] fragmentFile Fragment shader filename. + /** Create a new object. + \param[in] filename Shaders filename. + \param[in] vsEntry Vertex-shader entry point. If this string is empty (""), it will use a default vertex shader which transforms and outputs all default vertex attributes. + \param[in] psEntry Pixel shader entry point \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. \return A new object, or nullptr if creation failed. - - Note that this call merely creates a program object. The actual compilation and link happens when calling Program#getActiveVersion(). */ static SharedPtr createFromFile(const std::string& filename, const std::string& vsEntry, const std::string& psEntry, const DefineList& programDefines = DefineList()); private: GraphicsProgram() = default; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ParameterBlock.cpp b/Source/Falcor/Core/Program/ParameterBlock.cpp similarity index 83% rename from Framework/Source/Graphics/Program/ParameterBlock.cpp rename to Source/Falcor/Core/Program/ParameterBlock.cpp index 6e6ed31be..5b044a3a4 100644 --- a/Framework/Source/Graphics/Program/ParameterBlock.cpp +++ b/Source/Falcor/Core/Program/ParameterBlock.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,10 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ParameterBlock.h" -#include "API/Device.h" #include "Utils/StringUtils.h" +#include "Core/API/CopyContext.h" +#include "Core/API/Device.h" namespace Falcor { @@ -36,31 +37,31 @@ namespace Falcor { if (pVar == nullptr) { - logWarning(to_string(type) + " \"" + varName + "\" was not found. Ignoring " + funcName + " call."); + logError(to_string(type) + " \"" + varName + "\" was not found. Ignoring " + funcName + " call."); return false; } const ReflectionResourceType* pType = pVar->getType()->unwrapArray()->asResourceType(); if (!pType) { - logWarning(varName + " is not a resource. Ignoring " + funcName + " call."); + logError(varName + " is not a resource. Ignoring " + funcName + " call."); return false; } #if _LOG_ENABLED if (pType->getType() != type) { - logWarning("ParameterBlock::" + funcName + " was called, but variable \"" + varName + "\" has different resource type. Expecting + " + to_string(pType->getType()) + " but provided resource is " + to_string(type) + ". Ignoring call"); + logError("ParameterBlock::" + funcName + " was called, but variable \"" + varName + "\" has different resource type. Expecting + " + to_string(pType->getType()) + " but provided resource is " + to_string(type) + ". Ignoring call"); return false; } if (expectBuffer && pType->getDimensions() != ReflectionResourceType::Dimensions::Buffer) { - logWarning("ParameterBlock::" + funcName + " was called expecting a buffer variable, but the variable \"" + varName + "\" is not a buffer. Ignoring call"); + logError("ParameterBlock::" + funcName + " was called expecting a buffer variable, but the variable \"" + varName + "\" is not a buffer. Ignoring call"); return false; } if (access != ReflectionResourceType::ShaderAccess::Undefined && pType->getShaderAccess() != access) { - logWarning("ParameterBlock::" + funcName + " was called, but variable \"" + varName + "\" has different shader access type. Expecting + " + to_string(pType->getShaderAccess()) + " but provided resource is " + to_string(access) + ". Ignoring call"); + logError("ParameterBlock::" + funcName + " was called, but variable \"" + varName + "\" has different shader access type. Expecting + " + to_string(pType->getShaderAccess()) + " but provided resource is " + to_string(access) + ". Ignoring call"); return false; } #endif @@ -73,8 +74,16 @@ namespace Falcor { } - ParameterBlock::AssignedResource::AssignedResource(const AssignedResource& other) : pResource(other.pResource), type(other.type), pCB(nullptr) + ParameterBlock::AssignedResource::AssignedResource(const AssignedResource& other) { + *this = other; + } + + ParameterBlock::AssignedResource& ParameterBlock::AssignedResource::operator=(const AssignedResource& other) + { + pResource = other.pResource; + type = other.type; + switch (type) { case DescriptorSet::Type::Cbv: @@ -83,22 +92,25 @@ namespace Falcor case DescriptorSet::Type::Sampler: pSampler = other.pSampler; break; - case DescriptorSet::Type::StructuredBufferSrv: - case DescriptorSet::Type::TypedBufferSrv: case DescriptorSet::Type::TextureSrv: + case DescriptorSet::Type::RawBufferSrv: + case DescriptorSet::Type::TypedBufferSrv: + case DescriptorSet::Type::StructuredBufferSrv: pSRV = other.pSRV; break; - case DescriptorSet::Type::StructuredBufferUav: - case DescriptorSet::Type::TypedBufferUav: case DescriptorSet::Type::TextureUav: + case DescriptorSet::Type::RawBufferUav: + case DescriptorSet::Type::TypedBufferUav: + case DescriptorSet::Type::StructuredBufferUav: pUAV = other.pUAV; break; case DescriptorPool::Type::Count: - break; default: should_not_get_here(); break; } + + return *this; } ParameterBlock::AssignedResource::~AssignedResource() @@ -112,14 +124,16 @@ namespace Falcor case DescriptorSet::Type::Sampler: pSampler = nullptr; break; - case DescriptorSet::Type::StructuredBufferSrv: - case DescriptorSet::Type::TypedBufferSrv: case DescriptorSet::Type::TextureSrv: + case DescriptorSet::Type::RawBufferSrv: + case DescriptorSet::Type::TypedBufferSrv: + case DescriptorSet::Type::StructuredBufferSrv: pSRV = nullptr; break; - case DescriptorSet::Type::StructuredBufferUav: - case DescriptorSet::Type::TypedBufferUav: case DescriptorSet::Type::TextureUav: + case DescriptorSet::Type::RawBufferUav: + case DescriptorSet::Type::TypedBufferUav: + case DescriptorSet::Type::StructuredBufferUav: pUAV = nullptr; break; case DescriptorPool::Type::Count: @@ -159,11 +173,13 @@ namespace Falcor switch (d.type) { case DescriptorSet::Type::TextureSrv: + case DescriptorSet::Type::RawBufferSrv: case DescriptorSet::Type::TypedBufferSrv: case DescriptorSet::Type::StructuredBufferSrv: d.pSRV = ShaderResourceView::getNullView(); break; case DescriptorSet::Type::TextureUav: + case DescriptorSet::Type::RawBufferUav: case DescriptorSet::Type::TypedBufferUav: case DescriptorSet::Type::StructuredBufferUav: d.pUAV = UnorderedAccessView::getNullView(); @@ -189,15 +205,17 @@ namespace Falcor { range[r].requiredSize = resource.pType->getSize(); - if(createBuffers) + if (createBuffers) { if (resource.setType == DescriptorSet::Type::StructuredBufferSrv || resource.setType == DescriptorSet::Type::StructuredBufferUav) { + assert(resource.descCount == 1); // FIX ME: Arrays not supported StructuredBuffer::SharedPtr pBuffer = StructuredBuffer::create(resource.name, resource.pType); setStructuredBuffer(resource.name, pBuffer); } else if (resource.setType == DescriptorSet::Type::Cbv) { + assert(resource.descCount == 1); // FIX ME: Arrays not supported ConstantBuffer::SharedPtr pCB = ConstantBuffer::create(resource.name, resource.pType); setConstantBuffer(resource.name, pCB); } @@ -214,12 +232,12 @@ namespace Falcor if (pVar == nullptr) { - logWarning("Couldn't find a " + to_string(bufferType) + " named " + name); + logError("Couldn't find a " + to_string(bufferType) + " named " + name); return ProgramReflection::BindLocation(); } else if (pVar->getType()->unwrapArray()->asResourceType()->getType() != bufferType) { - logWarning("Found a variable named '" + name + "' but it is not a " + to_string(bufferType)); + logError("Found a variable named '" + name + "' but it is not a " + to_string(bufferType)); return ProgramReflection::BindLocation(); } @@ -239,7 +257,7 @@ namespace Falcor const auto& binding = getBufferBindLocation(mpReflector.get(), name, arrayIndex, ReflectionResourceType::Type::ConstantBuffer); if (binding.setIndex == ParameterBlockReflection::BindLocation::kInvalidLocation) { - logWarning("Constant buffer \"" + name + "\" was not found. Ignoring getConstantBuffer() call."); + logError("Constant buffer \"" + name + "\" was not found. Ignoring getConstantBuffer() call."); return nullptr; } return getConstantBuffer(binding, arrayIndex); @@ -255,7 +273,7 @@ namespace Falcor OK = OK && ((mAssignedResources[bindLocation.setIndex][bindLocation.rangeIndex][arrayIndex].type == type) || (type == DescriptorSet::Type::Count)); if (!OK) { - logWarning("Can't find resource at set index " + std::to_string(bindLocation.setIndex) + ", range index " + std::to_string(bindLocation.rangeIndex) + ", array index " + std::to_string(arrayIndex) + ". Ignoring " + funcName + " call"); + logError("Can't find resource at set index " + std::to_string(bindLocation.setIndex) + ", range index " + std::to_string(bindLocation.rangeIndex) + ", array index " + std::to_string(arrayIndex) + ". Ignoring " + funcName + " call"); } #endif return OK; @@ -294,7 +312,7 @@ namespace Falcor if (loc.setIndex == ParameterBlockReflection::BindLocation::kInvalidLocation) { - logWarning("Constant buffer \"" + name + "\" was not found. Ignoring setConstantBuffer() call."); + logError("Constant buffer \"" + name + "\" was not found. Ignoring setConstantBuffer() call."); return false; } return setConstantBuffer(loc, arrayIndex, pCB); @@ -329,14 +347,26 @@ namespace Falcor switch (type) { case DescriptorSet::Type::TextureSrv: + case DescriptorSet::Type::RawBufferSrv: case DescriptorSet::Type::TypedBufferSrv: case DescriptorSet::Type::StructuredBufferSrv: - desc.pSRV = pResource ? pResource->getSRV() : ShaderResourceView::getNullView(); + if (pResource != nullptr) + { + if (type == DescriptorSet::Type::TextureSrv) desc.pSRV = pResource->asTexture()->getSRV(); + else desc.pSRV = pResource->asBuffer()->getSRV(); + } + else desc.pSRV = ShaderResourceView::getNullView(); break; case DescriptorSet::Type::TextureUav: + case DescriptorSet::Type::RawBufferUav: case DescriptorSet::Type::TypedBufferUav: case DescriptorSet::Type::StructuredBufferUav: - desc.pUAV = pResource ? pResource->getUAV() : UnorderedAccessView::getNullView(); + if (pResource != nullptr) + { + if (type == DescriptorSet::Type::TextureUav) desc.pUAV = pResource->asTexture()->getUAV(); + else desc.pUAV = pResource->asBuffer()->getUAV(); + } + else desc.pUAV = UnorderedAccessView::getNullView(); break; default: should_not_get_here(); @@ -353,7 +383,7 @@ namespace Falcor return std::dynamic_pointer_cast(desc.pResource); } - bool ParameterBlock::setRawBuffer(const std::string& name, Buffer::SharedPtr pBuf) + bool ParameterBlock::setRawBuffer(const std::string& name, const Buffer::SharedPtr& pBuf) { // Find the buffer const ReflectionVar::SharedConstPtr pVar = mpReflector->getResource(name); @@ -364,12 +394,12 @@ namespace Falcor return false; } #endif - DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::TextureSrv, DescriptorSet::Type::TextureUav); + DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::RawBufferSrv, DescriptorSet::Type::RawBufferUav); setResourceSrvUavCommon(name, pVar->getDescOffset(), type, pBuf, "setRawBuffer()"); return true; } - bool ParameterBlock::setTypedBuffer(const std::string& name, TypedBufferBase::SharedPtr pBuf) + bool ParameterBlock::setTypedBuffer(const std::string& name, const TypedBufferBase::SharedPtr& pBuf) { // Find the buffer const ReflectionVar::SharedConstPtr pVar = mpReflector->getResource(name); @@ -384,7 +414,7 @@ namespace Falcor return true; } - bool ParameterBlock::setStructuredBuffer(const std::string& name, StructuredBuffer::SharedPtr pBuf) + bool ParameterBlock::setStructuredBuffer(const std::string& name, const StructuredBuffer::SharedPtr& pBuf) { const ReflectionVar::SharedConstPtr pVar = mpReflector->getResource(name); #if _LOG_ENABLED @@ -405,10 +435,11 @@ namespace Falcor #if _LOG_ENABLED if (verifyResourceVar(pVar.get(), ReflectionResourceType::Type::RawBuffer, ReflectionResourceType::ShaderAccess::Undefined, true, name, "getRawBuffer()") == false) { - return nullptr; + static Buffer::SharedPtr pNull = nullptr; + return pNull; } #endif - DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::TextureSrv, DescriptorSet::Type::TextureUav); + DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::RawBufferSrv, DescriptorSet::Type::RawBufferUav); return getResourceSrvUavCommon(name, pVar->getDescOffset(), type, "getRawBuffer()"); } @@ -419,7 +450,8 @@ namespace Falcor #if _LOG_ENABLED if (verifyResourceVar(pVar.get(), ReflectionResourceType::Type::TypedBuffer, ReflectionResourceType::ShaderAccess::Undefined, true, name, "getTypedBuffer()") == false) { - return nullptr; + static TypedBufferBase::SharedPtr pNull = nullptr; + return pNull; } #endif DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::TypedBufferSrv, DescriptorSet::Type::TypedBufferUav); @@ -433,7 +465,8 @@ namespace Falcor #if _LOG_ENABLED if (verifyResourceVar(pVar.get(), ReflectionResourceType::Type::StructuredBuffer, ReflectionResourceType::ShaderAccess::Undefined, true, name, "getStructuredBuffer()") == false) { - return nullptr; + static StructuredBuffer::SharedPtr pNull = nullptr; + return pNull; } #endif DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::StructuredBufferSrv, DescriptorSet::Type::StructuredBufferUav); @@ -469,16 +502,18 @@ namespace Falcor #if _LOG_ENABLED if (verifyResourceVar(pVar.get(), ReflectionResourceType::Type::Sampler, ReflectionResourceType::ShaderAccess::Read, false, name, "getSampler()") == false) { - return nullptr; + static Sampler::SharedPtr pNull = nullptr; + return pNull; } #endif ParameterBlockReflection::BindLocation bind = mpReflector->getResourceBinding(name); return getSampler(bind, pVar->getDescOffset()); } - Sampler::SharedPtr ParameterBlock::getSampler(const BindLocation& bindLocation, uint32_t arrayIndex) const + const Sampler::SharedPtr& ParameterBlock::getSampler(const BindLocation& bindLocation, uint32_t arrayIndex) const { - if (checkResourceIndices(bindLocation, arrayIndex, DescriptorSet::Type::Sampler, "getSampler()") == false) return nullptr; + static Sampler::SharedPtr pNull = nullptr; + if (checkResourceIndices(bindLocation, arrayIndex, DescriptorSet::Type::Sampler, "getSampler()") == false) return pNull; return mAssignedResources[bindLocation.setIndex][bindLocation.rangeIndex][arrayIndex].pSampler; } @@ -516,7 +551,8 @@ namespace Falcor #if _LOG_ENABLED if (verifyResourceVar(pVar.get(), ReflectionResourceType::Type::Texture, ReflectionResourceType::ShaderAccess::Undefined, false, name, "getTexture()") == false) { - return nullptr; + static Texture::SharedPtr pNull = nullptr; + return pNull; } #endif DescriptorSet::Type type = getSetTypeFromVar(pVar, DescriptorSet::Type::TextureSrv, DescriptorSet::Type::TextureUav); @@ -536,7 +572,7 @@ namespace Falcor #if _LOG_ENABLED if (desc.pSRV == nullptr) { - logWarning("Can't find SRV with set index " + std::to_string(bindLocation.setIndex) + ", range index " + std::to_string(bindLocation.rangeIndex) + ", array index " + std::to_string(arrayIndex) + ". Ignoring setSrv() call"); + logError("Can't find SRV with set index " + std::to_string(bindLocation.setIndex) + ", range index " + std::to_string(bindLocation.rangeIndex) + ", array index " + std::to_string(arrayIndex) + ". Ignoring setSrv() call"); return false; } #endif @@ -555,7 +591,7 @@ namespace Falcor #if _LOG_ENABLED if (desc.pUAV == nullptr) { - logWarning("Can't find UAV with set index " + std::to_string(bindLocation.setIndex) + ", range index " + std::to_string(bindLocation.rangeIndex) + ", array index " + std::to_string(arrayIndex) + ". Ignoring setUav() call"); + logError("Can't find UAV with set index " + std::to_string(bindLocation.setIndex) + ", range index " + std::to_string(bindLocation.rangeIndex) + ", array index " + std::to_string(arrayIndex) + ". Ignoring setUav() call"); return false; } #endif @@ -566,11 +602,12 @@ namespace Falcor mRootSets[bindLocation.setIndex].pSet = nullptr; return true; } - + static bool isUavSetType(DescriptorSet::Type type) { switch (type) { + case DescriptorSet::Type::RawBufferUav: case DescriptorSet::Type::StructuredBufferUav: case DescriptorSet::Type::TextureUav: case DescriptorSet::Type::TypedBufferUav: @@ -604,22 +641,28 @@ namespace Falcor if (isUav && pStructured->hasUAVCounter()) { pContext->resourceBarrier(pStructured->getUAVCounter().get(), Resource::State::UnorderedAccess); + pContext->uavBarrier(pStructured->getUAVCounter().get()); } } - + bool insertBarrier = true; + bool insertUavBarrier = isUav; #ifdef FALCOR_D3D12 insertBarrier = (is_set(pResource->getBindFlags(), Resource::BindFlags::AccelerationStructure) == false); #endif if (insertBarrier) { - pContext->resourceBarrier(pResource, isUav ? Resource::State::UnorderedAccess : Resource::State::ShaderResource); + if (pContext->resourceBarrier(pResource, isUav ? Resource::State::UnorderedAccess : Resource::State::ShaderResource)) + { + insertUavBarrier = false; + } } if (isUav) { if (pTypedBuffer) pTypedBuffer->setGpuCopyDirty(); if (pStructured) pStructured->setGpuCopyDirty(); + if (insertUavBarrier) pContext->uavBarrier(pResource); } return dirty; } @@ -665,7 +708,7 @@ namespace Falcor const auto& pDescSet = mRootSets[s].pSet; const auto& set = mAssignedResources[s]; - for (uint32_t r = 0 ; r < set.size() ; r++) + for (uint32_t r = 0; r < set.size(); r++) { const auto& range = set[r]; for (uint32_t d = 0; d < range.size(); d++) @@ -677,22 +720,24 @@ namespace Falcor { ConstantBuffer* pCB = dynamic_cast(desc.pResource.get()); ConstantBufferView::SharedPtr pView = pCB ? pCB->getCbv() : ConstantBufferView::getNullView(); - pDescSet->setCbv(r, d, pView); + pDescSet->setCbv(r, d, pView.get()); } break; case DescriptorSet::Type::Sampler: assert(desc.pSampler); pDescSet->setSampler(r, d, desc.pSampler.get()); break; - case DescriptorSet::Type::StructuredBufferSrv: - case DescriptorSet::Type::TypedBufferSrv: case DescriptorSet::Type::TextureSrv: + case DescriptorSet::Type::RawBufferSrv: + case DescriptorSet::Type::TypedBufferSrv: + case DescriptorSet::Type::StructuredBufferSrv: assert(desc.pSRV); pDescSet->setSrv(r, d, desc.pSRV.get()); break; - case DescriptorSet::Type::StructuredBufferUav: - case DescriptorSet::Type::TypedBufferUav: case DescriptorSet::Type::TextureUav: + case DescriptorSet::Type::RawBufferUav: + case DescriptorSet::Type::TypedBufferUav: + case DescriptorSet::Type::StructuredBufferUav: assert(desc.pUAV); pDescSet->setUav(r, d, desc.pUAV.get()); break; @@ -704,6 +749,13 @@ namespace Falcor } } } - return true; + ParameterBlock::SharedPtr p; + return true; + } + + bool ParameterBlock::setParameterBlock(const std::string& name, const std::shared_ptr& pBlock) + { + logError("Can't set a ParameterBlock into a ParameterBlock"); + return false; } -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ParameterBlock.h b/Source/Falcor/Core/Program/ParameterBlock.h similarity index 86% rename from Framework/Source/Graphics/Program/ParameterBlock.h rename to Source/Falcor/Core/Program/ParameterBlock.h index 4f43627f3..e656701e7 100644 --- a/Framework/Source/Graphics/Program/ParameterBlock.h +++ b/Source/Falcor/Core/Program/ParameterBlock.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,35 +26,24 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Program/ProgramReflection.h" -#include "API/ConstantBuffer.h" -#include "API/StructuredBuffer.h" -#include "API/TypedBuffer.h" +#include "Core/Program/ProgramReflection.h" +#include "Core/BufferTypes/ConstantBuffer.h" +#include "Core/BufferTypes/StructuredBuffer.h" +#include "Core/BufferTypes/TypedBuffer.h" +#include "Core/API/Texture.h" +#include "Core/API/Sampler.h" +#include "ProgramVarsHelpers.h" namespace Falcor { - class RootSignature; - class ProgramVars; - /** A parameter-block object. This object is stores all the resources and descriptor-sets required by a specific parameter-block in a program */ - class ParameterBlock : public std::enable_shared_from_this + class dlldecl ParameterBlock : public std::enable_shared_from_this, public IProgramVars { public: - template - class SharedPtrT : public std::shared_ptr - { - public: - SharedPtrT() : std::shared_ptr() {} - explicit SharedPtrT(T* pParamBlock) : std::shared_ptr(pParamBlock) {} - constexpr SharedPtrT(nullptr_t) : std::shared_ptr(nullptr) {} - SharedPtrT(const std::shared_ptr& other) : std::shared_ptr(other) {} - ConstantBuffer::SharedPtr operator[](const std::string& cbName) const { return std::shared_ptr::get()->getConstantBuffer(cbName); } - ConstantBuffer::SharedPtr operator[](uint32_t index) = delete; // No set by index. This is here because if we didn't explicitly delete it, the compiler will try to convert to int into a string, resulting in runtime error - }; - - using SharedPtr = SharedPtrT; - using SharedConstPtr = SharedPtrT; + using SharedPtr = VarsSharedPtr; + using SharedConstPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; ~ParameterBlock(); using BindLocation = ParameterBlockReflection::BindLocation; @@ -104,19 +93,19 @@ namespace Falcor \param[in] name The name of the buffer \param[in] pBuf The buffer object */ - bool setRawBuffer(const std::string& name, Buffer::SharedPtr pBuf); + bool setRawBuffer(const std::string& name, const Buffer::SharedPtr& pBuf); /** Set a typed buffer. Based on the shader reflection, it will be bound as either an SRV or a UAV \param[in] name The name of the buffer \param[in] pBuf The buffer object */ - bool setTypedBuffer(const std::string& name, TypedBufferBase::SharedPtr pBuf); + bool setTypedBuffer(const std::string& name, const TypedBufferBase::SharedPtr& pBuf); /** Set a structured buffer. Based on the shader reflection, it will be bound as either an SRV or a UAV \param[in] name The name of the buffer \param[in] pBuf The buffer object */ - bool setStructuredBuffer(const std::string& name, StructuredBuffer::SharedPtr pBuf); + bool setStructuredBuffer(const std::string& name, const StructuredBuffer::SharedPtr& pBuf); /** Get a raw-buffer object. \param[in] name The name of the buffer @@ -200,7 +189,7 @@ namespace Falcor \param[in] arrayIndex The array index, or 0 for non-arrays \return If the index is valid, a shared pointer to the sampler. Otherwise returns nullptr */ - Sampler::SharedPtr getSampler(const BindLocation& bindLocation, uint32_t arrayIndex) const; + const Sampler::SharedPtr& getSampler(const BindLocation& bindLocation, uint32_t arrayIndex) const; /** Get the program reflection interface */ @@ -212,7 +201,7 @@ namespace Falcor bool prepareForDraw(CopyContext* pContext); // Delete some functions. If they are not deleted, the compiler will try to convert the uints to string, resulting in runtime error - Sampler::SharedPtr getSampler(uint32_t) const = delete; + Sampler::SharedPtr getSampler(uint32_t) = delete; bool setSampler(uint32_t, const Sampler::SharedPtr&) = delete; bool setConstantBuffer(uint32_t, const ConstantBuffer::SharedPtr&) = delete; ConstantBuffer::SharedPtr getConstantBuffer(uint32_t) const = delete; @@ -229,23 +218,25 @@ namespace Falcor /** Get the root-sets */ std::vector& getRootSets() { return mRootSets; } + ParameterBlock::SharedPtr getVars() { return shared_from_this(); } private: ParameterBlock(const ParameterBlockReflection::SharedConstPtr& pReflection, bool createBuffers); ParameterBlockReflection::SharedConstPtr mpReflector; friend class ProgramVars; - struct AssignedResource + struct dlldecl AssignedResource { Resource::SharedPtr pResource = nullptr; AssignedResource(); AssignedResource(const AssignedResource& other); ~AssignedResource(); + AssignedResource& operator=(const AssignedResource& other); DescriptorSet::Type type; union { - ConstantBuffer::SharedPtr pCB; - ShaderResourceView::SharedPtr pSRV; + ConstantBuffer::SharedPtr pCB; + ShaderResourceView::SharedPtr pSRV; UnorderedAccessView::SharedPtr pUAV; Sampler::SharedPtr pSampler; }; @@ -260,5 +251,7 @@ namespace Falcor void setResourceSrvUavCommon(std::string name, uint32_t descOffset, DescriptorSet::Type type, const Resource::SharedPtr& pResource, const std::string& funcName); template typename ResourceType::SharedPtr getResourceSrvUavCommon(const std::string& name, uint32_t descOffset, DescriptorSet::Type type, const std::string& funcName) const; + + virtual bool setParameterBlock(const std::string& name, const std::shared_ptr& pBlock) override; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/Program.cpp b/Source/Falcor/Core/Program/Program.cpp similarity index 94% rename from Framework/Source/Graphics/Program/Program.cpp rename to Source/Falcor/Core/Program/Program.cpp index 1f29ae106..e65f8aacb 100644 --- a/Framework/Source/Graphics/Program/Program.cpp +++ b/Source/Falcor/Core/Program/Program.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Program.h" -#include -#include "glm/gtc/type_ptr.hpp" -#include "Graphics/TextureHelper.h" -#include "Utils/Platform/OS.h" -#include "API/Shader.h" -#include "Graphics/Program/ProgramVersion.h" -#include "API/Texture.h" -#include "API/Sampler.h" -#include "API/RenderContext.h" +#include "Slang/slang.h" #include "Utils/StringUtils.h" -#include "ShaderLibrary.h" namespace Falcor { @@ -102,7 +93,7 @@ namespace Falcor { // Don't set default vertex shader if one was set already. if (mEntryPoints[int(ShaderType::Vertex)].isValid()) return *this; - return addShaderLibrary("DefaultVS.slang").entryPoint(ShaderType::Vertex, "defaultVS"); + return addShaderLibrary("Raster.slang").entryPoint(ShaderType::Vertex, "defaultVS"); } const std::string& Program::Desc::getShaderEntryPoint(ShaderType shaderType) const @@ -320,16 +311,19 @@ namespace Falcor return false; } - ProgramVersion::SharedConstPtr Program::getActiveVersion() const + const ProgramVersion::SharedConstPtr& Program::getActiveVersion() const { - if(mLinkRequired) + if (mLinkRequired) { const auto& it = mProgramVersions.find(mDefineList); - if(it == mProgramVersions.end()) + if (it == mProgramVersions.end()) { - if(link() == false) + // Note that link() updates mActiveProgram only if the operation was successful. + // On error we get false, and mActiveProgram points to the last successfully compiled version. + if (link() == false) { - return nullptr; + static ProgramVersion::SharedConstPtr pNull = nullptr; + return pNull; } else { @@ -338,8 +332,9 @@ namespace Falcor } else { - mActiveProgram = mProgramVersions[mDefineList]; + mActiveProgram = it->second; } + mLinkRequired = false; } return mActiveProgram.pVersion; @@ -424,7 +419,7 @@ namespace Falcor // Pass any `#define` flags along to Slang, since we aren't doing our // own preprocessing any more. - for(auto shaderDefine : mDefineList) + for (auto shaderDefine : mDefineList) { spAddPreprocessorDefine(slangRequest, shaderDefine.first.c_str(), shaderDefine.second.c_str()); } @@ -464,6 +459,12 @@ namespace Falcor logWarning("Shader compiler flags 'FloatingPointModeFast' and 'FloatingPointModePrecise' can't be used simultaneously. Ignoring 'FloatingPointModeFast'."); flagFast = false; } + + if (is_set(mDesc.getCompilerFlags(), Shader::CompilerFlags::GenerateDebugInfo)) + { + spSetDebugInfoLevel(slangRequest, SLANG_DEBUG_INFO_LEVEL_STANDARD); + } + SlangFloatingPointMode slangFpMode = SLANG_FLOATING_POINT_MODE_DEFAULT; if (flagFast) slangFpMode = SLANG_FLOATING_POINT_MODE_FAST; else if (flagPrecise) slangFpMode = SLANG_FLOATING_POINT_MODE_PRECISE; @@ -498,7 +499,7 @@ namespace Falcor std::string fullpath; if (!findFileInDataDirectories(src.pLibrary->getFilename(), fullpath)) { - logError(std::string("Can't find file ") + src.pLibrary->getFilename(), true); + logError(std::string("Can't find file ") + src.pLibrary->getFilename()); return VersionData(); } spAddTranslationUnitSourceFile(slangRequest, translationUnitIndex, fullpath.c_str()); @@ -614,11 +615,11 @@ namespace Falcor { while(1) { - // create the program + // Create the program std::string log; VersionData programVersion = preprocessAndCreateProgramVersion(log); - if(programVersion.pVersion == nullptr) + if (programVersion.pVersion == nullptr) { std::string error = std::string("Program Linkage failed.\n\n"); error += getProgramDescString() + "\n"; @@ -661,4 +662,9 @@ namespace Falcor } } } + + SCRIPT_BINDING(Program) + { + m.regClass(Program); + } } diff --git a/Framework/Source/Graphics/Program/Program.h b/Source/Falcor/Core/Program/Program.h similarity index 92% rename from Framework/Source/Graphics/Program/Program.h rename to Source/Falcor/Core/Program/Program.h index 9d51dd1da..e9ebcc5cb 100644 --- a/Framework/Source/Graphics/Program/Program.h +++ b/Source/Falcor/Core/Program/Program.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,24 +26,21 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include -#include -#include "Graphics/Program//ProgramVersion.h" +#include "Core/API/Shader.h" +#include "Core/Program/ShaderLibrary.h" +#include "Core/Program/ProgramVersion.h" namespace Falcor { - class Shader; - class RenderContext; - class ShaderLibrary; - /** Common interface for modifying the macro definitions of programs. This is a workaround for the fact that RtProgram is currently unrelated to Program. */ - class ProgramBase + class dlldecl ProgramBase { public: using DefineList = Shader::DefineList; + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; /** Adds a macro definition to the program. If the macro already exists, it will be replaced. \param[in] name The name of define. @@ -92,7 +89,7 @@ namespace Falcor /** High-level abstraction of a program class. This class manages different versions of the same program. Different versions means same shader files, different macro definitions. This allows simple usage in case different macros are required - for example static vs. animated models. */ - class Program : public ProgramBase, public std::enable_shared_from_this + class dlldecl Program : public ProgramBase, public std::enable_shared_from_this { protected: static const uint32_t kShaderCount = (uint32_t)ShaderType::Count; @@ -105,7 +102,7 @@ namespace Falcor /** Description of a program to be created. */ - class Desc + class dlldecl Desc { public: /** Begin building a description, that initially has no source files or entry points. @@ -140,7 +137,7 @@ namespace Falcor /** Get the source library associated with a shader stage, or an empty library if one isn't bound to the shader */ - const std::shared_ptr& getShaderLibrary(ShaderType shaderType) const; + const ShaderLibrary::SharedPtr& getShaderLibrary(ShaderType shaderType) const; /** Get the source string associated with a shader stage, or an empty string if one isn't bound to the shader */ @@ -158,7 +155,6 @@ namespace Falcor */ Desc& dumpIntermediates(bool enable) { enable ? mShaderFlags |= Shader::CompilerFlags::DumpIntermediates : mShaderFlags &= ~(Shader::CompilerFlags::DumpIntermediates); return *this; } - /** Set the shader model string. This depends on the API you are using. For DirectX it should be `4_0`, `4_1`, `5_0`, `5_1`, `6_0`, `6_1` or `6_2`. The default is `5_1`. Shader model `6.x` will use dxcompiler For Vulkan, it should be `400`, `410`, `420`, `430`, `440` or `450`. The default is `450` @@ -186,11 +182,11 @@ namespace Falcor File }; - Source(std::shared_ptr pLib) : pLibrary(pLib), type(Type::File) {}; + Source(ShaderLibrary::SharedPtr pLib) : pLibrary(pLib), type(Type::File) {}; Source(std::string s) : str(s), type(Type::String) {}; Type type; - std::shared_ptr pLibrary; + ShaderLibrary::SharedPtr pLibrary; std::string str; }; @@ -217,7 +213,7 @@ namespace Falcor /** Get the API handle of the active program */ - ProgramVersion::SharedConstPtr getActiveVersion() const; + const ProgramVersion::SharedConstPtr& getActiveVersion() const; /** Adds a macro definition to the program. If the macro already exists, it will be replaced. \param[in] name The name of define. @@ -266,9 +262,9 @@ namespace Falcor */ static void reloadAllPrograms(); - const ProgramReflection::SharedConstPtr getReflector() const { getActiveVersion(); return mActiveProgram.reflectors.pReflector; } - const ProgramReflection::SharedConstPtr getLocalReflector() const { getActiveVersion(); return mActiveProgram.reflectors.pLocalReflector; } - const ProgramReflection::SharedConstPtr getGlobalReflector() const { getActiveVersion(); return mActiveProgram.reflectors.pGlobalReflector; } + const ProgramReflection::SharedPtr& getReflector() const { getActiveVersion(); return mActiveProgram.reflectors.pReflector; } + const ProgramReflection::SharedPtr& getLocalReflector() const { getActiveVersion(); return mActiveProgram.reflectors.pLocalReflector; } + const ProgramReflection::SharedPtr& getGlobalReflector() const { getActiveVersion(); return mActiveProgram.reflectors.pGlobalReflector; } protected: Program(); @@ -312,4 +308,4 @@ namespace Falcor bool checkIfFilesChanged(); void reset(); }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ProgramReflection.cpp b/Source/Falcor/Core/Program/ProgramReflection.cpp similarity index 91% rename from Framework/Source/Graphics/Program/ProgramReflection.cpp rename to Source/Falcor/Core/Program/ProgramReflection.cpp index ddd5ae535..b71c8107d 100644 --- a/Framework/Source/Graphics/Program/ProgramReflection.cpp +++ b/Source/Falcor/Core/Program/ProgramReflection.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ProgramReflection.h" #include "Utils/StringUtils.h" +#include "Slang/slang.h" +#include using namespace slang; namespace Falcor @@ -210,6 +212,19 @@ namespace Falcor case 4: return ReflectionBasicType::Type::Uint4; } + case TypeReflection::ScalarType::UInt64: + assert(rows == 1); + switch (columns) + { + case 1: + return ReflectionBasicType::Type::Uint64; + case 2: + return ReflectionBasicType::Type::Uint64_2; + case 3: + return ReflectionBasicType::Type::Uint64_3; + case 4: + return ReflectionBasicType::Type::Uint64_4; + } case TypeReflection::ScalarType::Int32: assert(rows == 1); switch (columns) @@ -223,6 +238,19 @@ namespace Falcor case 4: return ReflectionBasicType::Type::Int4; } + case TypeReflection::ScalarType::Int64: + assert(rows == 1); + switch (columns) + { + case 1: + return ReflectionBasicType::Type::Int64; + case 2: + return ReflectionBasicType::Type::Int64_2; + case 3: + return ReflectionBasicType::Type::Int64_3; + case 4: + return ReflectionBasicType::Type::Int64_4; + } case TypeReflection::ScalarType::Float32: switch (rows) { @@ -349,6 +377,18 @@ namespace Falcor return mod; } + // Determine if a Slang type layout consumes any storage/resources of the given kind + static bool hasUsage(slang::TypeLayoutReflection* pSlangTypeLayout, SlangParameterCategory resourceKind) + { + auto kindCount = pSlangTypeLayout->getCategoryCount(); + for(unsigned int ii = 0; ii < kindCount; ++ii) + { + if(pSlangTypeLayout->getCategoryByIndex(ii) == resourceKind) + return true; + } + return false; + } + static size_t getRegisterIndexFromPath(const ReflectionPath* pPath, SlangParameterCategory category) { uint32_t offset = 0; @@ -356,6 +396,31 @@ namespace Falcor { if (pp->pVar) { + // We are in the process of walking up from a leaf + // shader variable to the root (some global shader + // parameter). + // + // If along the way we run into a parameter block, + // *and* that parameter block has been allocated + // into its own register space, then we should stop + // ading contributions to the register/binding of + // the leaf parameter, since any register offsets + // coming from "above" this point shouldn't affect + // the register/binding of a parameter inside of + // the parameter block. + // + // TODO: This logic is really fiddly and doesn't + // seem like something Falcor should have to do. + // The Slang library should be provided utility + // functions to handle this stuff. + // + if(pp->pVar->getType()->getKind() == slang::TypeReflection::Kind::ParameterBlock + && hasUsage(pp->pVar->getTypeLayout(), SLANG_PARAMETER_CATEGORY_REGISTER_SPACE) + && category != SLANG_PARAMETER_CATEGORY_REGISTER_SPACE) + { + return offset; + } + offset += (uint32_t)pp->pVar->getOffset(category); continue; } @@ -394,6 +459,21 @@ namespace Falcor { if (pp->pVar) { + // Similar to the case above in `getRegisterIndexFromPath`, + // if we are walking from a member in a parameter block + // up to the block itself, then the space for our parameter + // should be offset by the register space assigned to + // the block itself, and we should stop walking up + // the breadcrumb trail. + // + // TODO: Just as in `getRegisterIndexFromPath` this is way + // too subtle, and Slang should be providing a service + // to compute this. + // + if(pp->pVar->getTypeLayout()->getKind() == slang::TypeReflection::Kind::ParameterBlock) + { + return offset + (uint32_t) getRegisterIndexFromPath(pp, SLANG_PARAMETER_CATEGORY_REGISTER_SPACE); + } offset += (uint32_t)pp->pVar->getBindingSpace(category); continue; } @@ -633,13 +713,15 @@ namespace Falcor return ProgramReflection::BindType::Cbv; case DescriptorSet::Type::Sampler: return ProgramReflection::BindType::Sampler; - case DescriptorSet::Type::StructuredBufferSrv: case DescriptorSet::Type::TextureSrv: + case DescriptorSet::Type::RawBufferSrv: case DescriptorSet::Type::TypedBufferSrv: + case DescriptorSet::Type::StructuredBufferSrv: return ProgramReflection::BindType::Srv; - case DescriptorSet::Type::StructuredBufferUav: case DescriptorSet::Type::TextureUav: + case DescriptorSet::Type::RawBufferUav: case DescriptorSet::Type::TypedBufferUav: + case DescriptorSet::Type::StructuredBufferUav: return ProgramReflection::BindType::Uav; default: should_not_get_here(); @@ -789,11 +871,15 @@ namespace Falcor break; case SLANG_STAGE_FRAGMENT: reflectShaderIO(pEntryPoint, SLANG_PARAMETER_CATEGORY_FRAGMENT_OUTPUT, mPsOut); - mIsSampleFrequency = pEntryPoint->usesAnySampleRateInput(); break; case SLANG_STAGE_VERTEX: reflectShaderIO(pEntryPoint, SLANG_PARAMETER_CATEGORY_VERTEX_INPUT, mVertAttr, &mVertAttrBySemantic); break; +#ifdef FALCOR_VK + mIsSampleFrequency = pEntryPoint->usesAnySampleRateInput(); +#else + mIsSampleFrequency = true; // #SLANG Slang reports false for DX shaders. There's an open issue, once it's fixed we should remove that +#endif default: break; } @@ -846,11 +932,11 @@ namespace Falcor return mResources.empty(); } - static ParameterBlockReflection::ResourceDesc getResourceDesc(const ReflectionVar::SharedConstPtr& pVar, uint32_t descCount, const std::string& name) + static ParameterBlockReflection::ResourceDesc getResourceDesc(const ReflectionVar::SharedConstPtr& pVar, uint32_t descCount, uint32_t descOffset, const std::string& name) { ParameterBlockReflection::ResourceDesc d; d.descCount = descCount; - d.descOffset = 0; + d.descOffset = descOffset; d.regIndex = pVar->getRegisterIndex(); d.regSpace = pVar->getRegisterSpace(); @@ -863,6 +949,8 @@ namespace Falcor d.setType = ParameterBlockReflection::ResourceDesc::Type::Cbv; break; case ReflectionResourceType::Type::RawBuffer: + d.setType = shaderAccess == ReflectionResourceType::ShaderAccess::Read ? ParameterBlockReflection::ResourceDesc::Type::RawBufferSrv : ParameterBlockReflection::ResourceDesc::Type::RawBufferUav; + break; case ReflectionResourceType::Type::Texture: d.setType = shaderAccess == ReflectionResourceType::ShaderAccess::Read ? ParameterBlockReflection::ResourceDesc::Type::TextureSrv : ParameterBlockReflection::ResourceDesc::Type::TextureUav; break; @@ -883,9 +971,9 @@ namespace Falcor return d; } - static void flattenResources(const ReflectionVar::SharedConstPtr& pVar, ParameterBlockReflection::ResourceVec& resources, uint32_t arrayElements, std::string name); + static void flattenResources(const ReflectionVar::SharedConstPtr& pVar, ParameterBlockReflection::ResourceVec& resources, uint32_t arrayElements, uint32_t descOffset, std::string name); - static void flattenResources(const ReflectionArrayType* pArrayType, ParameterBlockReflection::ResourceVec& resources, uint32_t arrayElements, std::string name) + static void flattenResources(const ReflectionArrayType* pArrayType, ParameterBlockReflection::ResourceVec& resources, uint32_t arrayElements, uint32_t descOffset, std::string name) { assert(pArrayType->asResourceType() == nullptr); if (pArrayType->getType()->asArrayType()) @@ -893,7 +981,7 @@ namespace Falcor pArrayType = pArrayType->getType()->asArrayType(); for (uint32_t i = 0; i < pArrayType->getArraySize(); i++) { - flattenResources(pArrayType, resources, 1, name + '[' + std::to_string(i) + ']'); + flattenResources(pArrayType, resources, 1, descOffset + i, name + '[' + std::to_string(i) + ']'); } } @@ -902,18 +990,18 @@ namespace Falcor { for (const auto& pMember : *pStructType) { - flattenResources(pMember, resources, 1, name + '.' + pMember->getName()); + flattenResources(pMember, resources, 1, descOffset, name + '.' + pMember->getName()); } } } - static void flattenResources(const ReflectionVar::SharedConstPtr& pVar, ParameterBlockReflection::ResourceVec& resources, uint32_t arrayElements, std::string name) + static void flattenResources(const ReflectionVar::SharedConstPtr& pVar, ParameterBlockReflection::ResourceVec& resources, uint32_t arrayElements, uint32_t descOffset, std::string name) { const ReflectionType* pType = pVar->getType()->unwrapArray(); uint32_t elementCount = max(1u, pVar->getType()->getTotalArraySize()) * arrayElements; if (pType->asResourceType()) { - resources.push_back(getResourceDesc(pVar, elementCount, name)); + resources.push_back(getResourceDesc(pVar, elementCount, descOffset, name)); return; } @@ -922,8 +1010,9 @@ namespace Falcor { for (uint32_t i = 0; i < pArrayType->getArraySize(); i++) { - flattenResources(pArrayType, resources, 1, name + '[' + std::to_string(i) + ']'); + flattenResources(pArrayType, resources, 1, descOffset + i, name + '[' + std::to_string(i) + ']'); } + return; } const ReflectionStructType* pStructType = pType->asStructType(); @@ -931,7 +1020,7 @@ namespace Falcor { for (const auto& pMember : *pStructType) { - flattenResources(pMember, resources, elementCount, name + '.' + pMember->getName()); + flattenResources(pMember, resources, elementCount, descOffset, name + '.' + pMember->getName()); } } } @@ -940,7 +1029,7 @@ namespace Falcor { for (const auto& pMember : *pStructType) { - flattenResources(pMember, resources, 1, pMember->getName()); + flattenResources(pMember, resources, 1, 0, pMember->getName()); } } @@ -976,7 +1065,7 @@ namespace Falcor // pVar might be a CB for a ParameterBlock. If it's empty, don't add it if(pStruct == nullptr || pStruct->getSize()) { - mResources.push_back(getResourceDesc(pVar, elementCount, pVar->getName())); + mResources.push_back(getResourceDesc(pVar, elementCount, 0, pVar->getName())); mpResourceVars->addMember(pVar); } @@ -1014,10 +1103,6 @@ namespace Falcor // Generate the descriptor sets layouts for (const auto& res : mResources) { - if (hasSuffix(res.name, ".layers")) - { - std::string a = res.name; - } SetIndex origIndex(res); uint32_t setIndex; if (newSetIndices.find(origIndex) == newSetIndices.end()) @@ -1084,7 +1169,6 @@ namespace Falcor if (index == kInvalidLocation) { static ParameterBlockReflection::SharedConstPtr pNull = nullptr; - logWarning("Can't find a parameter block named " + name); return pNull; } return mpParameterBlocks[index]; @@ -1094,7 +1178,6 @@ namespace Falcor { if (index >= mpParameterBlocks.size()) { - logWarning("Can't find a parameter block at index " + std::to_string(index)); static ParameterBlockReflection::SharedConstPtr pNull = nullptr; return pNull; } @@ -1109,21 +1192,12 @@ namespace Falcor ReflectionVar::SharedConstPtr ReflectionBasicType::findMemberInternal(const std::string& name, size_t strPos, size_t offset, uint32_t regIndex, uint32_t regSpace, uint32_t descOffset) const { // We shouldn't get here - logWarning("Can't find variable + " + name); return nullptr; } ReflectionVar::SharedConstPtr ReflectionResourceType::findMemberInternal(const std::string& name, size_t strPos, size_t offset, uint32_t regIndex, uint32_t regSpace, uint32_t descOffset) const { - if (mpStructType) - { - return mpStructType->findMemberInternal(name, strPos, offset, regIndex, regSpace, descOffset); - } - else - { - logWarning("Can't find variable '" + name + "'"); - return nullptr; - } + return mpStructType ? mpStructType->findMemberInternal(name, strPos, offset, regIndex, regSpace, descOffset) : nullptr; } ReflectionVar::SharedConstPtr ReflectionArrayType::findMemberInternal(const std::string& name, size_t strPos, size_t offset, uint32_t regIndex, uint32_t regSpace, uint32_t descOffset) const @@ -1195,11 +1269,7 @@ namespace Falcor size_t newPos = name.find_first_of(".[", strPos); std::string field = name.substr(strPos, newPos - strPos); size_t fieldIndex = getMemberIndex(field); - if (fieldIndex == ReflectionType::kInvalidOffset) - { - logWarning("Can't find variable '" + name + "'"); - return nullptr; - } + if (fieldIndex == ReflectionType::kInvalidOffset) return nullptr; const auto& pVar = getMember(fieldIndex); if (newPos == std::string::npos) return returnOrCreateVar(pVar, name, offset, regIndex, descOffset); diff --git a/Framework/Source/Graphics/Program/ProgramReflection.h b/Source/Falcor/Core/Program/ProgramReflection.h similarity index 96% rename from Framework/Source/Graphics/Program/ProgramReflection.h rename to Source/Falcor/Core/Program/ProgramReflection.h index 5941618f0..03a375219 100644 --- a/Framework/Source/Graphics/Program/ProgramReflection.h +++ b/Source/Falcor/Core/Program/ProgramReflection.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,11 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" -#include -#include -#include "Externals/Slang/slang.h" -#include "API/DescriptorSet.h" +#include "Core/API/DescriptorSet.h" + +namespace slang +{ + struct ShaderReflection; +}; namespace Falcor { @@ -42,7 +43,7 @@ namespace Falcor /** Base class for reflection types */ - class ReflectionType : public std::enable_shared_from_this + class dlldecl ReflectionType : public std::enable_shared_from_this { public: /** The type of the underlying type. When adding new derived classes, we'll need to update this enum @@ -106,11 +107,12 @@ namespace Falcor /** Reflection object for array-types */ - class ReflectionArrayType : public ReflectionType, inherit_shared_from_this + class dlldecl ReflectionArrayType : public ReflectionType, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; /** Create a new object */ @@ -145,11 +147,12 @@ namespace Falcor /** Reflection object for structs */ - class ReflectionStructType : public ReflectionType, inherit_shared_from_this + class dlldecl ReflectionStructType : public ReflectionType, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; /** Create a new object \param[in] offset The base offset of the object relative to the parent @@ -207,11 +210,12 @@ namespace Falcor /** Reflection object for scalars, vectors and matrices */ - class ReflectionBasicType : public ReflectionType, inherit_shared_from_this + class dlldecl ReflectionBasicType : public ReflectionType, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; /** The type of the object */ @@ -286,11 +290,12 @@ namespace Falcor /** Reflection object for resources */ - class ReflectionResourceType : public ReflectionType, public inherit_shared_from_this + class dlldecl ReflectionResourceType : public ReflectionType, public inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; /** Offset descriptor. Helper struct for constant- and structured-buffers. For valid offsets (which have matching fields in the buffer), the struct will contain the basic type and the number of elements (or 0 in case it's not an array) @@ -421,7 +426,7 @@ namespace Falcor /** An object describing a variable */ - class ReflectionVar + class dlldecl ReflectionVar { public: using SharedPtr = std::shared_ptr; @@ -491,7 +496,7 @@ namespace Falcor /** A reflection object describing a parameter-bloc */ - class ParameterBlockReflection + class dlldecl ParameterBlockReflection { public: using SharedPtr = std::shared_ptr; @@ -575,7 +580,7 @@ namespace Falcor /** Reflection object for an entire program. Essentially, it's a collection of ParameterBlocks */ - class ProgramReflection + class dlldecl ProgramReflection { public: using SharedPtr = std::shared_ptr; @@ -816,4 +821,4 @@ namespace Falcor } #undef type_2_string } -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ProgramVars.cpp b/Source/Falcor/Core/Program/ProgramVars.cpp similarity index 89% rename from Framework/Source/Graphics/Program/ProgramVars.cpp rename to Source/Falcor/Core/Program/ProgramVars.cpp index 1737fde76..e2f78a28a 100644 --- a/Framework/Source/Graphics/Program/ProgramVars.cpp +++ b/Source/Falcor/Core/Program/ProgramVars.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ProgramVars.h" -#include "API/Buffer.h" -#include "API/CopyContext.h" -#include "API/RenderContext.h" -#include "API/DescriptorSet.h" -#include "API/Device.h" -#include "Utils/StringUtils.h" +#include "GraphicsProgram.h" +#include "ComputeProgram.h" +#include "Core/API/ComputeContext.h" +#include "Core/API/RenderContext.h" namespace Falcor { @@ -89,6 +87,7 @@ namespace Falcor ProgramVars::ProgramVars(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers, const RootSignature::SharedPtr& pRootSig) : mpReflector(pReflector) { + assert(pReflector); mpRootSignature = pRootSig ? pRootSig : RootSignature::create(pReflector.get()); ParameterBlockReflection::SharedConstPtr pDefaultBlock = pReflector->getDefaultParameterBlock(); // Initialize the global-block first so that it's the first entry in the vector @@ -101,7 +100,7 @@ namespace Falcor mDefaultBlock = mParameterBlocks[mpReflector->getParameterBlockIndex("")]; } - const ParameterBlock::SharedConstPtr ProgramVars::getParameterBlock(const std::string& name) const + ParameterBlock::SharedPtr ProgramVars::getParameterBlock(const std::string& name) const { uint32_t index = mpReflector->getParameterBlockIndex(name); if (index == ProgramReflection::kInvalidLocation) @@ -112,32 +111,34 @@ namespace Falcor return mParameterBlocks[index].pBlock; } - const ParameterBlock::SharedConstPtr ProgramVars::getParameterBlock(uint32_t blockIndex) const + ParameterBlock::SharedPtr ProgramVars::getParameterBlock(uint32_t blockIndex) const { return (blockIndex < mParameterBlocks.size()) ? mParameterBlocks[blockIndex].pBlock : nullptr; } - void ProgramVars::setParameterBlock(const std::string& name, const ParameterBlock::SharedConstPtr& pBlock) + bool ProgramVars::setParameterBlock(const std::string& name, const std::shared_ptr& pBlock) { uint32_t index = mpReflector->getParameterBlockIndex(name); if (index == ProgramReflection::kInvalidLocation) { logWarning("Can't find parameter block named " + name + ". Ignoring setParameterBlock() call"); - return; + return false; } mParameterBlocks[index].bind = true; - mParameterBlocks[index].pBlock = pBlock ? std::const_pointer_cast(pBlock) : ParameterBlock::create(mpReflector->getParameterBlock(index), true); // #PARAMBLOCK + mParameterBlocks[index].pBlock = pBlock ? pBlock : ParameterBlock::create(mpReflector->getParameterBlock(index), true); + return true; } - void ProgramVars::setParameterBlock(uint32_t blockIndex, const ParameterBlock::SharedConstPtr& pBlock) + bool ProgramVars::setParameterBlock(uint32_t blockIndex, const std::shared_ptr& pBlock) { if (blockIndex >= mParameterBlocks.size()) { logWarning("setParameterBlock() - block index out-of-bounds"); - return; + return false; } mParameterBlocks[blockIndex].bind = true; - mParameterBlocks[blockIndex].pBlock = pBlock ? std::const_pointer_cast(pBlock) : ParameterBlock::create(mpReflector->getParameterBlock(blockIndex), true); // #PARAMBLOCK + mParameterBlocks[blockIndex].pBlock = pBlock ? pBlock : ParameterBlock::create(mpReflector->getParameterBlock(blockIndex), true); + return true; } GraphicsVars::SharedPtr GraphicsVars::create(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers, const RootSignature::SharedPtr& pRootSig) @@ -145,11 +146,21 @@ namespace Falcor return SharedPtr(new GraphicsVars(pReflector, createBuffers, pRootSig)); } + GraphicsVars::SharedPtr GraphicsVars::create(const GraphicsProgram* pProg) + { + return create(pProg->getReflector()); + } + ComputeVars::SharedPtr ComputeVars::create(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers, const RootSignature::SharedPtr& pRootSig) { return SharedPtr(new ComputeVars(pReflector, createBuffers, pRootSig)); } + ComputeVars::SharedPtr ComputeVars::create(const ComputeProgram* pProg) + { + return create(pProg->getReflector()); + } + ConstantBuffer::SharedPtr ProgramVars::getConstantBuffer(const std::string& name) const { return mDefaultBlock.pBlock->getConstantBuffer(name); @@ -172,17 +183,17 @@ namespace Falcor return mDefaultBlock.pBlock->setConstantBuffer(name, pCB); } - bool ProgramVars::setRawBuffer(const std::string& name, Buffer::SharedPtr pBuf) + bool ProgramVars::setRawBuffer(const std::string& name, const Buffer::SharedPtr& pBuf) { return mDefaultBlock.pBlock->setRawBuffer(name, pBuf); } - bool ProgramVars::setTypedBuffer(const std::string& name, TypedBufferBase::SharedPtr pBuf) + bool ProgramVars::setTypedBuffer(const std::string& name, const TypedBufferBase::SharedPtr& pBuf) { return mDefaultBlock.pBlock->setTypedBuffer(name, pBuf); } - bool ProgramVars::setStructuredBuffer(const std::string& name, StructuredBuffer::SharedPtr pBuf) + bool ProgramVars::setStructuredBuffer(const std::string& name, const StructuredBuffer::SharedPtr& pBuf) { return mDefaultBlock.pBlock->setStructuredBuffer(name, pBuf); } @@ -195,7 +206,7 @@ namespace Falcor TypedBufferBase::SharedPtr ProgramVars::getTypedBuffer(const std::string& name) const { return mDefaultBlock.pBlock->getTypedBuffer(name); - } + } StructuredBuffer::SharedPtr ProgramVars::getStructuredBuffer(const std::string& name) const { @@ -320,4 +331,4 @@ namespace Falcor { return applyProgramVarsCommon(pContext, bindRootSig); } -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ProgramVars.h b/Source/Falcor/Core/Program/ProgramVars.h similarity index 87% rename from Framework/Source/Graphics/Program/ProgramVars.h rename to Source/Falcor/Core/Program/ProgramVars.h index b4c926c5c..b3e4af72e 100644 --- a/Framework/Source/Graphics/Program/ProgramVars.h +++ b/Source/Falcor/Core/Program/ProgramVars.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,40 +26,24 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/Texture.h" -#include "API/ConstantBuffer.h" -#include "API/Sampler.h" -#include -#include "ProgramReflection.h" -#include "API/StructuredBuffer.h" -#include "API/TypedBuffer.h" -#include "API/LowLevel/RootSignature.h" -#include "Graphics/Program/ParameterBlock.h" +#include "Core/BufferTypes/ConstantBuffer.h" +#include "Core/BufferTypes/StructuredBuffer.h" +#include "Core/Program/ParameterBlock.h" +#include "Core/API/RootSignature.h" +#include "ProgramVarsHelpers.h" namespace Falcor { - class ProgramVersion; + class GraphicsProgram; + class ComputeProgram; class ComputeContext; - class DescriptorSet; /** This class manages a program's reflection and variable assignment. It's a high-level abstraction of variables-related concepts such as CBs, texture and sampler assignments, root-signature, descriptor tables, etc. */ - class ProgramVars + class dlldecl ProgramVars : public IProgramVars { public: - template - class SharedPtrT : public std::shared_ptr - { - public: - SharedPtrT() : std::shared_ptr() {} - explicit SharedPtrT(T* pProgVars) : std::shared_ptr(pProgVars) {} - constexpr SharedPtrT(nullptr_t) : std::shared_ptr(nullptr) {} - SharedPtrT(const std::shared_ptr& other) : std::shared_ptr(other) {} - ConstantBuffer::SharedPtr operator[](const std::string& cbName) { return std::shared_ptr::get()->getConstantBuffer(cbName); } - ConstantBuffer::SharedPtr operator[](uint32_t index) = delete; // No set by index. This is here because if we didn't explicitly delete it, the compiler will try to convert to int into a string, resulting in runtime error - }; - /** Bind a constant buffer object by name. If the name doesn't exists or the CBs size doesn't match the required size, the call will fail. If a buffer was previously bound it will be released. @@ -101,21 +85,21 @@ namespace Falcor \param[in] pBuf The buffer object \return false is the call failed, otherwise true */ - bool setRawBuffer(const std::string& name, Buffer::SharedPtr pBuf); + bool setRawBuffer(const std::string& name, const Buffer::SharedPtr& pBuf); /** Set a typed buffer. Based on the shader reflection, it will be bound as either an SRV or a UAV \param[in] name The name of the buffer \param[in] pBuf The buffer object \return false is the call failed, otherwise true */ - bool setTypedBuffer(const std::string& name, TypedBufferBase::SharedPtr pBuf); + bool setTypedBuffer(const std::string& name, const TypedBufferBase::SharedPtr& pBuf); /** Set a structured buffer. Based on the shader reflection, it will be bound as either an SRV or a UAV \param[in] name The name of the buffer \param[in] pBuf The buffer object \return false is the call failed, otherwise true */ - bool setStructuredBuffer(const std::string& name, StructuredBuffer::SharedPtr pBuf); + bool setStructuredBuffer(const std::string& name, const StructuredBuffer::SharedPtr& pBuf); /** Get a raw-buffer object. \param[in] name The name of the buffer @@ -220,11 +204,11 @@ namespace Falcor /** Get the program reflection interface */ - ProgramReflection::SharedConstPtr getReflection() const { return mpReflector; } + const ProgramReflection::SharedConstPtr& getReflection() const { return mpReflector; } /** Get the root signature object */ - RootSignature::SharedPtr getRootSignature() const { return mpRootSignature; } + const RootSignature::SharedPtr& getRootSignature() const { return mpRootSignature; } /** Get the number of parameter-blocks */ @@ -236,19 +220,19 @@ namespace Falcor /** Get parameter-block by index. You can translate a name to an index using the ProgramReflection object */ - const ParameterBlock::SharedConstPtr getParameterBlock(uint32_t blockIndex) const; + ParameterBlock::SharedPtr getParameterBlock(uint32_t blockIndex) const; /** Get parameter-block by name */ - const ParameterBlock::SharedConstPtr getParameterBlock(const std::string& name) const; + ParameterBlock::SharedPtr getParameterBlock(const std::string& name) const; /** Set parameter-block by index. You can translate a name to an index using the ProgramReflection object */ - void setParameterBlock(uint32_t blockIndex, const ParameterBlock::SharedConstPtr& pBlock); + bool setParameterBlock(uint32_t blockIndex, const std::shared_ptr& pBlock); /** Set parameter-block by name */ - void setParameterBlock(const std::string& name, const ParameterBlock::SharedConstPtr& pBlock); + bool setParameterBlock(const std::string& name, const std::shared_ptr& pBlock); /** Get the default parameter-block */ @@ -275,6 +259,7 @@ namespace Falcor std::vector rootIndex; // Maps the block's set-index to the root-signature entry bool bind = true; }; + BlockData mDefaultBlock; std::vector mParameterBlocks; // First element is the global block ProgramVars::BlockData initParameterBlock(const ParameterBlockReflection::SharedConstPtr& pBlockReflection, bool createBuffers); @@ -283,10 +268,10 @@ namespace Falcor bool bindRootSetsCommon(CopyContext* pContext, bool bindRootSig); }; - class GraphicsVars : public ProgramVars, public std::enable_shared_from_this + class dlldecl GraphicsVars : public ProgramVars, public std::enable_shared_from_this { public: - using SharedPtr = SharedPtrT; + using SharedPtr = VarsSharedPtr; using SharedConstPtr = std::shared_ptr; /** Create a new object @@ -295,16 +280,18 @@ namespace Falcor \param[in] pRootSignature A root-signature describing how to bind resources into the shader. If this parameter is nullptr, a root-signature object will be created from the program reflection object */ static SharedPtr create(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers = true, const RootSignature::SharedPtr& pRootSig = nullptr); + static SharedPtr create(const GraphicsProgram* pProg); virtual bool apply(RenderContext* pContext, bool bindRootSig); + SharedPtr getVars() { return shared_from_this(); } protected: GraphicsVars(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers, const RootSignature::SharedPtr& pRootSig) : ProgramVars(pReflector, createBuffers, pRootSig) {} }; - class ComputeVars : public ProgramVars, public std::enable_shared_from_this + class dlldecl ComputeVars : public ProgramVars, public std::enable_shared_from_this { public: - using SharedPtr = SharedPtrT; + using SharedPtr = VarsSharedPtr; using SharedConstPtr = std::shared_ptr; /** Create a new object @@ -313,9 +300,11 @@ namespace Falcor \param[in] pRootSignature A root-signature describing how to bind resources into the shader. If this parameter is nullptr, a root-signature object will be created from the program reflection object */ static SharedPtr create(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers = true, const RootSignature::SharedPtr& pRootSig = nullptr); + static SharedPtr create(const ComputeProgram* pProg); virtual bool apply(ComputeContext* pContext, bool bindRootSig); + SharedPtr getVars() { return shared_from_this(); } protected: ComputeVars(const ProgramReflection::SharedConstPtr& pReflector, bool createBuffers, const RootSignature::SharedPtr& pRootSig) : ProgramVars(pReflector, createBuffers, pRootSig) {} }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Core/Program/ProgramVarsHelpers.h b/Source/Falcor/Core/Program/ProgramVarsHelpers.h new file mode 100644 index 000000000..52f0c028c --- /dev/null +++ b/Source/Falcor/Core/Program/ProgramVarsHelpers.h @@ -0,0 +1,159 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/API/Texture.h" +#include "Core/API/Sampler.h" +#include "Core/API/Buffer.h" +#include "Core/BufferTypes/StructuredBuffer.h" +#include "Core/BufferTypes/TypedBuffer.h" + +namespace Falcor +{ + class ConstantBuffer; + class ProgramVars; + class ParameterBlock; + + class dlldecl IProgramVars + { + public: + virtual ~IProgramVars() = default; + virtual bool setConstantBuffer(const std::string& name, const ConstantBuffer::SharedPtr& pCB) = 0; + virtual ConstantBuffer::SharedPtr getConstantBuffer(const std::string& name) const = 0; + virtual bool setRawBuffer(const std::string& name, const Buffer::SharedPtr& pBuf) = 0; + virtual bool setTypedBuffer(const std::string& name, const TypedBufferBase::SharedPtr& pBuf) = 0; + virtual bool setStructuredBuffer(const std::string& name, const StructuredBuffer::SharedPtr& pBuf) = 0; + virtual bool setSampler(const std::string& name, const Sampler::SharedPtr& pSampler) = 0; + virtual bool setParameterBlock(const std::string& name, const std::shared_ptr& pBlock) = 0; + + virtual Buffer::SharedPtr getRawBuffer(const std::string& name) const = 0; + virtual TypedBufferBase::SharedPtr getTypedBuffer(const std::string& name) const = 0; + virtual StructuredBuffer::SharedPtr getStructuredBuffer(const std::string& name) const = 0; + virtual bool setTexture(const std::string& name, const Texture::SharedPtr& pTexture) = 0; + virtual Texture::SharedPtr getTexture(const std::string& name) const = 0; + virtual Sampler::SharedPtr getSampler(const std::string& name) const = 0; + + // Delete some functions. If they are not deleted, the compiler will try to convert the uints to string, resulting in runtime error + // MAKE SURE TO ALSO DELETE THEM IN THE DERIVED CLASS! + virtual Sampler::SharedPtr getSampler(uint32_t) const = delete; + virtual bool setSampler(uint32_t, const Sampler::SharedPtr&) = delete; + virtual bool setConstantBuffer(uint32_t, const ConstantBuffer::SharedPtr&) = delete; + virtual ConstantBuffer::SharedPtr getConstantBuffer(uint32_t) const = delete; + }; + + class dlldecl IndexedVars + { + public: + virtual IProgramVars* getVars() const = 0; + + /** A secondary intermediary class that allows a double [][] operator to be used on the SharedPtr. + */ + class Var + { + public: + /** Constructor gets called when mySharedPtr["myIdx1"]["myVar"] is encountered + */ + Var(ConstantBuffer::SharedPtr pCB, const std::string& name) : mpCB(pCB), mName(name) {} + + /** Assignment operator gets called when mySharedPtr["myIdx1"]["myVar"] = T(someData); is encountered + */ + template void operator=(const T& val) { if (mpCB) mpCB[mName] = val; } + + /** Allows mySharedPtr["myIdx1"]["myVar"].setBlob( blobData )... + In theory, block binary transfers could be done with operator=, but without careful coding that could *accidentally* do implicit binary transfers + */ + template void setBlob(const T& blob, size_t blobSz = sizeof(T)) { if (mpCB) mpCB[mName].setBlob(blob, blobSz); } + protected: + ConstantBuffer::SharedPtr mpCB; ///< Storing the constant buffer from mySharedPtr["myIdx1"] + const std::string mName; ///< When calling mySharedPtr["myIdx1"]["myVar"] this stores "myVar" + }; + + /** An intermediary class that allows the [] operator to be used on the SharedPtr. + */ + class Index + { + public: + /** Constructor gets called when mySharedPtr["myIdx1"] is encountered + */ + Index(IProgramVars* pVars, const std::string& name) : mpVars(pVars), mName(name) { } + + /** When a second array operator is encountered, instantiate a Var object to handle mySharedPtr["myIdx1"]["myVar"] + */ + Var operator[](const std::string& var) { return mpVars ? Var(mpVars->getConstantBuffer(mName), var) : Var(nullptr, var); } + + Var operator[](uint32_t index) = delete; // No set by index. This is here because if we didn't explicitly delete it, the compiler will try to convert to int into a string, resulting in runtime error + + /** When encountering an assignment operator (i.e., mySharedPtr["myIdx1"] = pSomeResource;) + set the appropriate resource for the following types: texture, sampler, various buffers + Note: When compiling in Release mode, these fail silently if your specified shader resource + is of the wrong type. In Debug mode, you hit an assert in the appropriate operator=() so you + can check which variable is screwed up. TODO: Log errors rather than assert()ing. + */ + void operator=(const Texture::SharedPtr& pTexture) { mpVars->setTexture(mName, pTexture); } + void operator=(const Sampler::SharedPtr& pSampler) { mpVars->setSampler(mName, pSampler); } + + void operator=(const TypedBufferBase::SharedPtr& pBuffer) { mpVars->setTypedBuffer(mName, pBuffer); } + void operator=(const StructuredBuffer::SharedPtr& pBuffer) { mpVars->setStructuredBuffer(mName, pBuffer); } + void operator=(const Buffer::SharedPtr pBuffer) { mpVars->setRawBuffer(mName, pBuffer); } + void operator=(const std::shared_ptr& pBlock) { mpVars->setParameterBlock(mName, pBlock); } + + /** Allow conversion of this intermediary type to a constant buffer, e.g., for allowing: + ConstantBuffer::SharedPtr cb = mySharedPtr["myIdx1"]; + */ + operator ConstantBuffer::SharedPtr() { return mpVars->getConstantBuffer(mName); } + + void setBlob(const void* pSrc, size_t offset, size_t size) + { + auto pCb = mpVars->getConstantBuffer(mName); + if (pCb) pCb->setBlob(pSrc, offset, size); + } + + template + void setBlob(const BlobData& src, size_t offset = 0) { return setBlob(&src, offset, sizeof(BlobData)); } + + protected: + IProgramVars* mpVars; ///< The variables wrapper we're using + const std::string mName; ///< When calling mySharedPtr["myIdx1"], stores "myIdx1" + }; + + /** Calling [] on the SharedPtr. Create an intermediate object to process further operators + */ + Index operator[](const std::string& var) const { return Index(getVars(), var); } + }; + + template + class VarsSharedPtr : public std::shared_ptr, public IndexedVars + { + public: + VarsSharedPtr() : std::shared_ptr(), IndexedVars() {} + explicit VarsSharedPtr(Class* pProgVars) : std::shared_ptr(pProgVars) {} + constexpr VarsSharedPtr(nullptr_t) : std::shared_ptr(nullptr) {} + VarsSharedPtr(const std::shared_ptr& other) : std::shared_ptr(other) {} + + IProgramVars* getVars() const override { return std::shared_ptr::get()->getVars().get(); } + }; +} diff --git a/Framework/Source/Graphics/Program/ProgramVersion.cpp b/Source/Falcor/Core/Program/ProgramVersion.cpp similarity index 89% rename from Framework/Source/Graphics/Program/ProgramVersion.cpp rename to Source/Falcor/Core/Program/ProgramVersion.cpp index a6ffaf1b5..e8d33acee 100644 --- a/Framework/Source/Graphics/Program/ProgramVersion.cpp +++ b/Source/Falcor/Core/Program/ProgramVersion.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Graphics/Program/ProgramVersion.h" +#include "stdafx.h" +#include "ProgramVersion.h" namespace Falcor { - ProgramVersion::ProgramVersion(const ProgramReflection::SharedPtr& pReflector, const Shader::SharedPtr& pVS, const Shader::SharedPtr& pPS, const Shader::SharedPtr& pGS, const Shader::SharedPtr& pHS, const Shader::SharedPtr& pDS, const Shader::SharedPtr& pCS, const std::string& name) + ProgramVersion::ProgramVersion(const ProgramReflection::SharedPtr& pReflector, const Shader::SharedPtr& pVS, const Shader::SharedPtr& pPS, const Shader::SharedPtr& pGS, const Shader::SharedPtr& pHS, const Shader::SharedPtr& pDS, const Shader::SharedPtr& pCS, const std::string& name) : mName(name), mpReflector(pReflector) { mpShaders[(uint32_t)ShaderType::Vertex] = pVS; @@ -58,12 +58,6 @@ namespace Falcor return nullptr; } SharedPtr pProgram = SharedPtr(new ProgramVersion(pReflector, pVS, pPS, pGS, pHS, pDS, nullptr, name)); - - if(pProgram->init(log) == false) - { - return nullptr; - } - return pProgram; } @@ -80,16 +74,6 @@ namespace Falcor return nullptr; } SharedPtr pProgram = SharedPtr(new ProgramVersion(pReflector, nullptr, nullptr, nullptr, nullptr, nullptr, pCS, name)); - - if (pProgram->init(log) == false) - { - return nullptr; - } return pProgram; } - - ProgramVersion::~ProgramVersion() - { - deleteApiHandle(); - } } diff --git a/Framework/Source/Graphics/Program/ProgramVersion.h b/Source/Falcor/Core/Program/ProgramVersion.h similarity index 91% rename from Framework/Source/Graphics/Program/ProgramVersion.h rename to Source/Falcor/Core/Program/ProgramVersion.h index 33a7626fc..3455d109f 100644 --- a/Framework/Source/Graphics/Program/ProgramVersion.h +++ b/Source/Falcor/Core/Program/ProgramVersion.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,21 +26,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" -#include -#include -#include -#include "API/Shader.h" -#include "Graphics/Program//ProgramReflection.h" +#include "Core/Program/ProgramReflection.h" +#include "Core/API/Shader.h" namespace Falcor { - class ConstantBuffer; - /** Low-level program object This class abstracts the API's program creation and management */ - class ProgramVersion : public std::enable_shared_from_this + class dlldecl ProgramVersion : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -80,7 +74,7 @@ namespace Falcor std::string& log, const std::string& name = ""); - virtual ~ProgramVersion(); + virtual ~ProgramVersion() = default; /** Get an attached shader object, or nullptr if no shader is attached to the slot. */ @@ -103,8 +97,6 @@ namespace Falcor const Shader::SharedPtr& pCS, const std::string& name = ""); - virtual bool init(std::string& log); - void deleteApiHandle(); ProgramHandle mApiHandle = ProgramHandle(); const std::string mName; @@ -114,4 +106,4 @@ namespace Falcor void* mpPrivateData; const ProgramReflection::SharedPtr mpReflector; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ShaderLibrary.cpp b/Source/Falcor/Core/Program/ShaderLibrary.cpp similarity index 95% rename from Framework/Source/Graphics/Program/ShaderLibrary.cpp rename to Source/Falcor/Core/Program/ShaderLibrary.cpp index 3e07d1df3..8bb632202 100644 --- a/Framework/Source/Graphics/Program/ShaderLibrary.cpp +++ b/Source/Falcor/Core/Program/ShaderLibrary.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,10 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ShaderLibrary.h" namespace Falcor { -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Program/ShaderLibrary.h b/Source/Falcor/Core/Program/ShaderLibrary.h similarity index 94% rename from Framework/Source/Graphics/Program/ShaderLibrary.h rename to Source/Falcor/Core/Program/ShaderLibrary.h index 4e1aabc16..bcec17c40 100644 --- a/Framework/Source/Graphics/Program/ShaderLibrary.h +++ b/Source/Falcor/Core/Program/ShaderLibrary.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,11 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" namespace Falcor { - class ShaderLibrary + class dlldecl ShaderLibrary { public: using SharedPtr = std::shared_ptr; @@ -44,4 +43,4 @@ namespace Falcor ShaderLibrary(const std::string& filename) : mFilename(filename) {} std::string mFilename; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Renderer.h b/Source/Falcor/Core/Renderer.h similarity index 50% rename from Framework/Source/Renderer.h rename to Source/Falcor/Core/Renderer.h index 18b84b8b3..73fcded34 100644 --- a/Framework/Source/Renderer.h +++ b/Source/Falcor/Core/Renderer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,82 +26,61 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once +#include "Utils/UI/UserInput.h" +#include "Utils/ArgList.h" namespace Falcor { - class Gui; - class Window; - class RenderContext; - class Fbo; - class SampleTest; - class ArgList; - - class SampleCallbacks + class Clock; + class FrameRate; + + /** Sample configuration + */ + struct SampleConfig : ScriptBindings::enable_to_string + { + Window::Desc windowDesc; ///< Controls window creation + Device::Desc deviceDesc; ///< Controls device creation; + bool showMessageBoxOnError = true; ///< Show message box on framework/API errors. + float timeScale = 1.0f; ///< A scaling factor for the time elapsed between frames + bool pauseTime = false; ///< Control whether or not to start the clock when the sample start running + bool showUI = true; ///< Show the UI + }; + + class IFramework { public: /** Get the render-context for the current frame. This might change each frame*/ virtual RenderContext* getRenderContext() = 0; /** Get the current FBO*/ - virtual std::shared_ptr getCurrentFbo() = 0; + virtual std::shared_ptr getTargetFbo() = 0; /** Get the window*/ virtual Window* getWindow() = 0; - /** Get the default GUI*/ - virtual Gui* getGui() = 0; - - /** Get the current time*/ - virtual float getCurrentTime() = 0; + /** Get the global Clock object + */ + virtual Clock& getGlobalClock() = 0; - /** Set the current time*/ - virtual void setCurrentTime(float time) = 0; + /** Get the global FrameRate object + */ + virtual FrameRate& getFrameRate() = 0; /** Resize the swap-chain buffers*/ virtual void resizeSwapChain(uint32_t width, uint32_t height) = 0; - /** Get the average framerate */ - virtual float getFrameRate() = 0; - - /** Get the last frame time */ - virtual float getLastFrameTime() = 0; - - /** Get the current frame ID*/ - virtual uint64_t getFrameID() = 0; - - /** Render text */ - virtual void renderText(const std::string& str, const glm::vec2& position, glm::vec2 shadowOffset = glm::vec2(1)) = 0; - - /** Get a string with the framerate information */ - virtual std::string getFpsMsg() = 0; - /** Check if a key is pressed*/ virtual bool isKeyPressed(const KeyboardEvent::Key& key) = 0; - /** Show/hide text */ - virtual void toggleText(bool showText) = 0; - /** Show/hide the UI */ virtual void toggleUI(bool showUI) = 0; - /** Show/hide the globalUI */ - virtual void toggleGlobalUI(bool showGlobalUI) = 0; - - /** Set the default GUI size */ - virtual void setDefaultGuiSize(uint32_t width, uint32_t height) = 0; - - /** Set the default GUI position*/ - virtual void setDefaultGuiPosition(uint32_t x, uint32_t y) = 0; + /** Show/hide the UI */ + virtual bool isUiEnabled() = 0; /** Get the object storing command line arguments */ virtual ArgList getArgList() = 0; - /** Specify the delta time for deterministic testing */ - virtual void setFixedTimeDelta(float newDelta) = 0; - - /** Get the fixed delta time */ - virtual float getFixedTimeDelta() = 0; - /** Takes and outputs a screenshot. */ virtual std::string captureScreen(const std::string explicitFilename = "", const std::string explicitOutputDirectory = "") = 0; @@ -110,87 +89,88 @@ namespace Falcor */ virtual void shutdown() = 0; - /** Stop the timer + /** Pause/resume the renderer. The GUI will still be rendered */ - virtual void freezeTime(bool timeFrozen) = 0; + virtual void pauseRenderer(bool pause) = 0; - /** Check if the clock is ticking + /** Check if the renderer running */ - virtual bool isTimeFrozen() = 0; + virtual bool isRendererPaused() = 0; - /** Check if the rendering should be reset. - This can be useful for reseting the temporal accumulation at the beginning of recording a video, for example. + /** Get the current configuration */ - virtual bool shouldResetRendering() = 0; + virtual SampleConfig getConfig() = 0; + + /** Render the global UI. You'll can open a GUI window yourself before calling it + */ + virtual void renderGlobalUI(Gui* pGui) = 0; + + /** Get the global shortcuts message + */ + virtual std::string getKeyboardShortcutsStr() = 0; + + /** Set VSYNC + */ + virtual void toggleVsync(bool on) = 0; + + /** Get the VSYNC state + */ + virtual bool isVsyncEnabled() = 0; }; - class Renderer : std::enable_shared_from_this + dlldecl extern IFramework* gpFramework; + + class IRenderer { public: - using UniquePtr = std::unique_ptr; - Renderer() = default; - virtual ~Renderer() {}; + using UniquePtr = std::unique_ptr; + IRenderer() = default; + virtual ~IRenderer() {}; /** Called once right after context creation. */ - virtual void onLoad(SampleCallbacks* pCallbacks, RenderContext* pRenderContext) {} + virtual void onLoad(RenderContext* pRenderContext) {} /** Called on each frame render. */ - virtual void onFrameRender(SampleCallbacks* pCallbacks, RenderContext* pRenderContext, const std::shared_ptr& pTargetFbo) {} + virtual void onFrameRender(RenderContext* pRenderContext, const std::shared_ptr& pTargetFbo) {} /** Called right before the context is destroyed. */ - virtual void onShutdown(SampleCallbacks* pCallbacks) {} + virtual void onShutdown() {} /** Called every time the swap-chain is resized. You can query the default FBO for the new size and sample count of the window. */ - virtual void onResizeSwapChain(SampleCallbacks* pCallbacks, uint32_t width, uint32_t height) {} + virtual void onResizeSwapChain(uint32_t width, uint32_t height) {} /** Called every time the user requests shader recompilation (by pressing F5) */ - virtual void onDataReload(SampleCallbacks* pCallbacks) {} + virtual void onDataReload() {} /** Called every time a key event occurred. \param[in] keyEvent The keyboard event \return true if the event was consumed by the callback, otherwise false */ - virtual bool onKeyEvent(SampleCallbacks* pCallbacks, const KeyboardEvent& keyEvent) { return false; } + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) { return false; } /** Called every time a mouse event occurred. \param[in] mouseEvent The mouse event \return true if the event was consumed by the callback, otherwise false */ - virtual bool onMouseEvent(SampleCallbacks* pCallbacks, const MouseEvent& mouseEvent) { return false; } + virtual bool onMouseEvent(const MouseEvent& mouseEvent) { return false; } /** Called after onFrameRender(). It is highly recommended to use onGuiRender() exclusively for GUI handling. onGuiRender() will not be called when the GUI is hidden, which should help reduce CPU overhead. You could also ignore this and render the GUI directly in your onFrameRender() function, but that is discouraged. */ - virtual void onGuiRender(SampleCallbacks* pCallbacks, Gui* pGui) {}; + virtual void onGuiRender(Gui* pGui) {}; /** Called when a file is dropped into the window */ - virtual void onDroppedFile(SampleCallbacks* pCallbacks, const std::string& filename) {} - - /** Callback for anything the tested renderer needs to do to initialize testing - Will only be called if testing has been activated - */ - virtual void onInitializeTesting(SampleCallbacks* pCallbacks) {}; - - /** Callback for anything the tested renderer wants to do on test frames - Will only be called if testing frames exist. - Called after onFrameRender() and before onGuiRender(). - */ - virtual void onTestFrame(SampleCallbacks* pSampleTest) {}; - - /** Callback for anything the tested renderer wants to do right before shutdown - Will only be called if testing frames exist - */ - virtual void onTestShutdown(SampleCallbacks* pSampleTest) {}; + virtual void onDroppedFile(const std::string& filename) {} // Deleted copy operators (copy a pointer type!) - Renderer(const Renderer&) = delete; - Renderer& operator=(const Renderer &) = delete; + IRenderer(const IRenderer&) = delete; + IRenderer& operator=(const IRenderer &) = delete; }; } diff --git a/Source/Falcor/Core/Sample.cpp b/Source/Falcor/Core/Sample.cpp new file mode 100644 index 000000000..4420bfbee --- /dev/null +++ b/Source/Falcor/Core/Sample.cpp @@ -0,0 +1,640 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Sample.h" +#include "RenderGraph/RenderPassLibrary.h" +#include "Core/Platform/ProgressBar.h" +#include "Utils/StringUtils.h" +#include +#include +#include "Utils/Threading.h" +#include "dear_imgui/imgui.h" + +namespace Falcor +{ + IFramework* gpFramework = nullptr; + namespace + { + std::string kMonospaceFont = "monospace"; + } + + void Sample::handleWindowSizeChange() + { + if (!gpDevice) return; + // Tell the device to resize the swap chain + auto winSize = mpWindow->getClientAreaSize(); + auto pBackBufferFBO = gpDevice->resizeSwapChain(winSize.x, winSize.y); + auto width = pBackBufferFBO->getWidth(); + auto height = pBackBufferFBO->getHeight(); + + // Recreate target fbo + auto pCurrentFbo = mpTargetFBO; + mpTargetFBO = Fbo::create2D(width, height, pBackBufferFBO->getDesc()); + gpDevice->getRenderContext()->blit(pCurrentFbo->getColorTexture(0)->getSRV(), mpTargetFBO->getRenderTargetView(0)); + + // Tell the GUI the swap-chain size changed + if(mpGui) mpGui->onWindowResize(width, height); + + // Resize the pixel zoom + if(mpPixelZoom) mpPixelZoom->onResizeSwapChain(gpDevice->getSwapChainFbo().get()); + + // Call the user callback + if(mpRenderer) mpRenderer->onResizeSwapChain(width, height); + } + + void Sample::handleKeyboardEvent(const KeyboardEvent& keyEvent) + { + if (keyEvent.type == KeyboardEvent::Type::KeyPressed) mPressedKeys.insert(keyEvent.key); + else if (keyEvent.type == KeyboardEvent::Type::KeyReleased) mPressedKeys.erase(keyEvent.key); + + if (mShowUI && mpGui->onKeyboardEvent(keyEvent)) return; + if (mpRenderer && mpRenderer->onKeyEvent(keyEvent)) return; + + // Checks if should toggle zoom + mpPixelZoom->onKeyboardEvent(keyEvent); + + // Consume system messages first + if (keyEvent.type == KeyboardEvent::Type::KeyPressed) + { + if (keyEvent.mods.isShiftDown && keyEvent.key == KeyboardEvent::Key::F12) + { + initVideoCapture(); + } + else if (keyEvent.mods.isCtrlDown) + { + switch (keyEvent.key) + { + case KeyboardEvent::Key::Pause: + mRendererPaused = !mRendererPaused; + break; + } + } + else if (!keyEvent.mods.isAltDown && !keyEvent.mods.isCtrlDown && !keyEvent.mods.isShiftDown) + { + switch (keyEvent.key) + { + case KeyboardEvent::Key::F12: + mCaptureScreen = true; + break; +#if _PROFILING_ENABLED + case KeyboardEvent::Key::P: + gProfileEnabled = !gProfileEnabled; + break; +#endif + case KeyboardEvent::Key::V: + mVsyncOn = !mVsyncOn; + gpDevice->toggleVSync(mVsyncOn); + mFrameRate.reset(); + mClock.now(0); + break; + case KeyboardEvent::Key::F2: + toggleUI(!mShowUI); + break; + case KeyboardEvent::Key::F5: + Program::reloadAllPrograms(); + if (mpRenderer) mpRenderer->onDataReload(); + break; + case KeyboardEvent::Key::Escape: + if (mVideoCapture.pVideoCapture) + { + endVideoCapture(); + } + else + { + mpWindow->shutdown(); + } + break; + case KeyboardEvent::Key::Pause: + mClock.isPaused() ? mClock.play() : mClock.pause(); + break; + } + } + } + } + + void Sample::handleDroppedFile(const std::string& filename) + { + if(mpRenderer) mpRenderer->onDroppedFile(filename); + } + + void Sample::handleMouseEvent(const MouseEvent& mouseEvent) + { + if (mShowUI && mpGui->onMouseEvent(mouseEvent)) return; + if (mpRenderer && mpRenderer->onMouseEvent(mouseEvent)) return; + if (mpPixelZoom->onMouseEvent(mouseEvent)) return; + } + + // Sample functions + Sample::~Sample() + { + if (mVideoCapture.pVideoCapture) endVideoCapture(); + + Clock::shutdown(); + Threading::shutdown(); + Scripting::shutdown(); + RenderPassLibrary::instance().shutdown(); + TextRenderer::shutdown(); + mpGui.reset(); + mpTargetFBO.reset(); + mpPixelZoom.reset(); + if(gpDevice) gpDevice->cleanup(); + gpDevice.reset(); + OSServices::stop(); + } + + void Sample::run(const SampleConfig& config, IRenderer::UniquePtr& pRenderer, uint32_t argc, char** argv) + { + Sample s(pRenderer); + s.runInternal(config, argc, argv); + } + + void Sample::run(const std::string& filename, IRenderer::UniquePtr& pRenderer, uint32_t argc, char** argv) + { + auto err = [filename](std::string_view msg) {logError("Error in Sample::Run(). `" + filename + "` " + msg); }; + Sample s(pRenderer); + + s.startScripting(); // We have to do that before running the script + std::string fullpath; + SampleConfig c; + if (findFileInDataDirectories(filename, fullpath)) + { + Scripting::Context ctx; + std::string errorLog; + try + { + Scripting::runScriptFromFile(fullpath, ctx); + auto configs = ctx.getObjects(); + if (configs.empty()) err("doesn't contain any SampleConfig objects"); + else + { + if (configs.size() > 1) err("contains multiple SampleConfig objects. Using the first one"); + c = configs[0]; + } + } + catch (std::exception e) + { + err("\n" + errorLog); + } + } + else + { + err(" doesn't exist. Using default configuration"); + } + s.runInternal(c, argc, argv); + } + + void Sample::runInternal(const SampleConfig& config, uint32_t argc, char** argv) + { + gpFramework = this; + + OSServices::start(); + startScripting(); + Threading::start(); + + mShowUI = config.showUI; + mClock.timeScale(config.timeScale); + if (config.pauseTime) mClock.pause(); + mVsyncOn = config.deviceDesc.enableVsync; + Logger::showBoxOnError(config.showMessageBoxOnError); + + // Create the window + mpWindow = Window::create(config.windowDesc, this); + if (mpWindow == nullptr) + { + logError("Failed to create device and window"); + return; + } + + // Show the progress bar + ProgressBar::MessageList msgList = + { + { "Initializing Falcor" }, + { "Takes a while, doesn't it?" }, + { "Don't get too bored now" }, + { "Getting there" }, + { "Loading. Seriously, loading" }, + { "Are we there yet?"}, + { "NI!"} + }; + + ProgressBar::SharedPtr pBar = ProgressBar::show(msgList); + + Device::Desc d = config.deviceDesc; + gpDevice = Device::create(mpWindow, config.deviceDesc); + if (gpDevice == nullptr) + { + logError("Failed to create device"); + return; + } + + Clock::start(); + + // Get the default objects before calling onLoad() + auto pBackBufferFBO = gpDevice->getSwapChainFbo(); + mpTargetFBO = Fbo::create2D(pBackBufferFBO->getWidth(), pBackBufferFBO->getHeight(), pBackBufferFBO->getDesc()); + + // Init the UI + initUI(); + mpPixelZoom = PixelZoom::create(mpTargetFBO.get()); + +#ifdef _WIN32 + // Set the icon + setWindowIcon("Framework\\Nvidia.ico", mpWindow->getApiHandle()); + + if (argc == 0 || argv == nullptr) + { + mArgList.parseCommandLine(GetCommandLineA()); + } + else +#endif + { + mArgList.parseCommandLine(concatCommandLine(argc, argv)); + } + + // Load and run + mpRenderer->onLoad(getRenderContext()); + pBar = nullptr; + + mFrameRate.reset(); + mpWindow->msgLoop(); + + mpRenderer->onShutdown(); + if (gpDevice) gpDevice->flushAndSync(); + mpRenderer = nullptr; + Logger::shutdown(); + } + + void screenSizeUI(Gui::Widgets& widget, uvec2 screenDims) + { + static const uvec2 resolutions[] = + { + {0, 0}, + {1280, 720}, + {1920, 1080}, + {1920, 1200}, + {2560, 1440}, + {3840, 2160}, + }; + + static const auto initDropDown = [](const uvec2 resolutions[], uint32_t count) -> Gui::DropdownList + { + Gui::DropdownList list; + for (uint32_t i = 0 ; i < count; i++) + { + list.push_back({ i, to_string(resolutions[i].x) + "x" + to_string(resolutions[i].y) }); + } + list[0] = { 0, "Custom" }; + return list; + }; + + auto initDropDownVal = [](const uvec2 resolutions[], uint32_t count, uvec2 screenDims) + { + for (uint32_t i = 0; i < count; i++) + { + if (screenDims == resolutions[i]) return i; + } + return 0u; + }; + + static const Gui::DropdownList dropdownList = initDropDown(resolutions, arraysize(resolutions)); + uint32_t currentVal = initDropDownVal(resolutions, arraysize(resolutions), screenDims); + + widget.var("Screen Resolution", screenDims); + if (widget.dropdown("Change Resolution", dropdownList, currentVal) && (currentVal != 0)) gpFramework->resizeSwapChain(resolutions[currentVal].x, resolutions[currentVal].y); + } + + std::string Sample::getKeyboardShortcutsStr() + { + constexpr char help[] = + " 'F2' - Show\\Hide GUI\n" + " 'F5' - Reload shaders\n" + " 'ESC' - Quit\n" + " 'V' - Toggle VSync\n" + " 'F12' - Capture screenshot\n" + " 'Shift+F12' - Video capture\n" + " 'Pause' - Pause\\resume the global timer\n" + " 'Ctrl+Pause' - Pause\\resume the renderer\n" + " 'Z' - Zoom in on a pixel\n" + " 'MouseWheel' - Change level of zoom\n" +#if _PROFILING_ENABLED + " 'P' - Enable profiling\n" +#endif + ; + + return help; + } + + void Sample::renderGlobalUI(Gui* pGui) + { + ImGui::TextUnformatted("Keyboard Shortcuts"); + ImGui::SameLine(); + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(450.0f); + ImGui::TextUnformatted(getKeyboardShortcutsStr().c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + auto controlsGroup = Gui::Group(pGui, "Global Controls"); + if (controlsGroup.open()) + { + float t = (float)mClock.now(); + if (controlsGroup.var("Time", t, 0.f, FLT_MAX)) mClock.now(double(t)); + if (controlsGroup.button("Reset")) mClock.now(0.0); + bool timePaused = mClock.isPaused(); + if (controlsGroup.button(timePaused ? "Play" : "Pause", true)) timePaused ? mClock.pause() : mClock.play(); + if (controlsGroup.button("Stop", true)) mClock.stop(); + + float scale = (float)mClock.timeScale(); + if (controlsGroup.var("Scale", scale, 0.f, FLT_MAX)) mClock.timeScale(scale); + controlsGroup.separator(); + + if (controlsGroup.button(mRendererPaused ? "Resume Rendering" : "Pause Rendering")) mRendererPaused = !mRendererPaused; + controlsGroup.tooltip("Freeze the renderer and keep displaying the last rendered frame. The renderer will keep accepting mouse/keyboard/GUI messages. Changes in the UI will not be reflected in the displayed image until the renderer is unfrozen"); + + controlsGroup.separator(); + screenSizeUI(controlsGroup, mpWindow->getClientAreaSize()); + controlsGroup.separator(); + + mCaptureScreen = controlsGroup.button("Screen Capture"); + if (controlsGroup.button("Video Capture", true)) initVideoCapture(); + if (controlsGroup.button("Save Config")) saveConfigToFile(); + + controlsGroup.release(); + } + + } + + void Sample::renderUI() + { + PROFILE("renderUI"); + + if (mShowUI || gProfileEnabled) + { + mpGui->beginFrame(); + + if(mShowUI) mpRenderer->onGuiRender(mpGui.get()); + if (mVideoCapture.displayUI && mVideoCapture.pUI) + { + Gui::Window w(mpGui.get(), "Video Capture", mVideoCapture.displayUI, { 20, 300 }, { 300, 280 }); + mVideoCapture.pUI->render(w); + } + + if (gProfileEnabled) + { + uint32_t y = gpDevice->getSwapChainFbo()->getHeight() - 360; + + mpGui->setActiveFont(kMonospaceFont); + + Gui::Window profilerWindow(mpGui.get(), "Profiler", gProfileEnabled, { 800, 350 }, { 10, y }); + Profiler::endEvent("renderUI"); // Stop the timer + + if(gProfileEnabled) + { + profilerWindow.text(Profiler::getEventsString().c_str()); + Profiler::startEvent("renderUI"); + profilerWindow.release(); + } + mpGui->setActiveFont(""); + } + + mpGui->render(getRenderContext(), gpDevice->getSwapChainFbo(), (float)mFrameRate.getLastFrameTime()); + } + } + + void Sample::renderFrame() + { + if (gpDevice && gpDevice->isWindowOccluded()) return; + + mClock.tick(); + mFrameRate.newFrame(); + if (mVideoCapture.fixedTimeDelta) { mClock.now(mVideoCapture.currentTime); } + + { + PROFILE("onFrameRender"); + + // The swap-chain FBO might have changed between frames, so get it + if (!mRendererPaused) + { + RenderContext* pRenderContext = gpDevice ? gpDevice->getRenderContext() : nullptr; + mpRenderer->onFrameRender(pRenderContext, mpTargetFBO); + } + } + + if (gpDevice) + { + // Copy the render-target + auto pSwapChainFbo = gpDevice->getSwapChainFbo(); + RenderContext* pRenderContext = getRenderContext(); + pRenderContext->copyResource(pSwapChainFbo->getColorTexture(0).get(), mpTargetFBO->getColorTexture(0).get()); + + // Capture video frame before UI is rendered + bool captureVideoUI = mVideoCapture.pUI && mVideoCapture.pUI->captureUI(); // Check capture mode here once only, as its value may change after renderGUI() + if (!captureVideoUI) captureVideoFrame(); + renderUI(); + + pSwapChainFbo = gpDevice->getSwapChainFbo(); // The UI might have triggered a swap-chain resize, invalidating the previous FBO + if (mpPixelZoom) mpPixelZoom->render(pRenderContext, pSwapChainFbo.get()); + +#if _PROFILING_ENABLED + Profiler::endFrame(); +#endif + // Capture video frame after UI is rendered + if (captureVideoUI) captureVideoFrame(); + if (mCaptureScreen) captureScreen(); + + { + PROFILE("present", Profiler::Flags::Internal); + gpDevice->present(); + } + } + + Console::flush(); + } + + std::string Sample::captureScreen(const std::string explicitFilename, const std::string explicitOutputDirectory) + { + mCaptureScreen = false; + + std::string filename = explicitFilename != "" ? explicitFilename : getExecutableName(); + std::string outputDirectory = explicitOutputDirectory != "" ? explicitOutputDirectory : getExecutableDirectory(); + + std::string pngFile; + if (findAvailableFilename(filename, outputDirectory, "png", pngFile)) + { + Texture::SharedPtr pTexture; + pTexture = gpDevice->getSwapChainFbo()->getColorTexture(0); + pTexture->captureToFile(0, 0, pngFile); + } + else + { + logError("Could not find available filename when capturing screen"); + return ""; + } + + return pngFile; + } + + void Sample::initUI() + { + float scaling = getDisplayScaleFactor(); + const auto& pSwapChainFbo = gpDevice->getSwapChainFbo(); + mpGui = Gui::create(uint32_t(pSwapChainFbo->getWidth()), uint32_t(pSwapChainFbo->getHeight()), scaling); + mpGui->addFont(kMonospaceFont, "Framework/Fonts/consolab.ttf"); + TextRenderer::start(); + } + + void Sample::resizeSwapChain(uint32_t width, uint32_t height) + { + mpWindow->resize(width, height); + } + + bool Sample::isKeyPressed(const KeyboardEvent::Key& key) + { + return mPressedKeys.find(key) != mPressedKeys.cend(); + } + + void Sample::initVideoCapture() + { + if (mVideoCapture.pUI == nullptr) + { + mVideoCapture.pUI = VideoEncoderUI::create([this]() {startVideoCapture(); }, [this]() {endVideoCapture(); }); + } + mVideoCapture.displayUI = true; + } + + void Sample::startVideoCapture() + { + // Create the Capture Object and Framebuffer. + VideoEncoder::Desc desc; + desc.flipY = false; + desc.codec = mVideoCapture.pUI->getCodec(); + desc.filename = mVideoCapture.pUI->getFilename(); + const auto& pSwapChainFbo = gpDevice->getSwapChainFbo(); + desc.format = pSwapChainFbo->getColorTexture(0)->getFormat(); + desc.fps = mVideoCapture.pUI->getFPS(); + desc.height = pSwapChainFbo->getHeight(); + desc.width = pSwapChainFbo->getWidth(); + desc.bitrateMbps = mVideoCapture.pUI->getBitrate(); + desc.gopSize = mVideoCapture.pUI->getGopSize(); + + mVideoCapture.pVideoCapture = VideoEncoder::create(desc); + + assert(mVideoCapture.pVideoCapture); + mVideoCapture.pFrame.resize(desc.width*desc.height * 4); + mVideoCapture.fixedTimeDelta = 1 / (double)desc.fps; + + if (mVideoCapture.pUI->useTimeRange()) + { + if (mVideoCapture.pUI->getStartTime() > mVideoCapture.pUI->getEndTime()) + { + mVideoCapture.fixedTimeDelta = -mVideoCapture.fixedTimeDelta; + } + mVideoCapture.currentTime = mVideoCapture.pUI->getStartTime(); + } + } + + void Sample::endVideoCapture() + { + if (mVideoCapture.pVideoCapture) + { + mVideoCapture.pVideoCapture->endCapture(); + mShowUI = true; + } + mVideoCapture = {}; + } + + void Sample::captureVideoFrame() + { + if (mVideoCapture.pVideoCapture) + { + mVideoCapture.pVideoCapture->appendFrame(getRenderContext()->readTextureSubresource(gpDevice->getSwapChainFbo()->getColorTexture(0).get(), 0).data()); + + if (mVideoCapture.pUI->useTimeRange()) + { + if (mVideoCapture.fixedTimeDelta >= 0) + { + if (mVideoCapture.currentTime >= mVideoCapture.pUI->getEndTime()) endVideoCapture(); + } + else if (mVideoCapture.currentTime < mVideoCapture.pUI->getEndTime()) + { + endVideoCapture(); + } + } + + mVideoCapture.currentTime += mVideoCapture.fixedTimeDelta; + } + } + + SampleConfig Sample::getConfig() + { + SampleConfig c; + c.deviceDesc = gpDevice->getDesc(); + c.windowDesc = mpWindow->getDesc(); + c.showMessageBoxOnError = Logger::isBoxShownOnError(); + c.timeScale = (float)mClock.timeScale(); + c.pauseTime = mClock.isPaused(); + c.showUI = mShowUI; + return c; + } + + void Sample::saveConfigToFile() + { + std::string filename; + if (saveFileDialog(Scripting::kFileExtensionFilters, filename)) + { + SampleConfig c = getConfig(); + std::string s = "sampleConfig = " + ScriptBindings::to_string(c) + "\n"; + std::ofstream(filename) << s; + } + } + + void Sample::startScripting() + { + Scripting::start(); + auto bindFunc = [this](ScriptBindings::Module& m) { this->registerScriptBindings(m); }; + ScriptBindings::registerBinding(bindFunc); + } + + void Sample::registerScriptBindings(ScriptBindings::Module& m) + { + auto sampleDesc = m.regClass(SampleConfig); +#define field(f_) rwField(#f_, &SampleConfig::f_) + sampleDesc.field(windowDesc).field(deviceDesc).field(showMessageBoxOnError).field(timeScale); + sampleDesc.field(pauseTime).field(showUI); +#undef field + auto exit = [](int32_t errorCode) {postQuitMessage(errorCode); }; + m.func_("exit", exit, "errorCode"_a = 0); + + auto renderFrame = [this]() {ProgressBar::close(); this->renderFrame(); }; + m.func_("renderFrame", renderFrame); + } +} diff --git a/Source/Falcor/Core/Sample.h b/Source/Falcor/Core/Sample.h new file mode 100644 index 000000000..91af9deea --- /dev/null +++ b/Source/Falcor/Core/Sample.h @@ -0,0 +1,148 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Window.h" +#include "API/Device.h" +#include "Renderer.h" +#include "Utils/ArgList.h" +#include "Utils/Timing/FrameRate.h" +#include "Utils/UI/Gui.h" +#include "Utils/UI/TextRenderer.h" +#include "Utils/UI/PixelZoom.h" +#include "Utils/Video/VideoEncoderUI.h" +#include +#include + +namespace Falcor +{ + /** Bootstrapper class for Falcor + Call Sample::run() to start the sample. + The render loop will then call the user's Renderer object + */ + class dlldecl Sample : public Window::ICallbacks, public IFramework + { + public: + /** Entry-point to Sample. User should call this to start processing. + \param[in] config Requested sample configuration + \param[in] pRenderer The user's renderer + \param[in] argc Optional. The number of strings in `argv` + \param[in] argv Optional. The command line arguments + Note that when running a Windows application (with WinMain()), the command line arguments will be retrieved and parsed even if argc and argv are nullptr + */ + static void run(const SampleConfig& config, IRenderer::UniquePtr& pRenderer, uint32_t argc = 0, char** argv = nullptr); + + /** Entry-point to Sample. User should call this to start processing. + \param[in] filename A filename containing the sample configuration. If the file is not found, the sample will issue an error and lunch with the default configuration + \param[in] pRenderer The user's renderer + \param[in] argc Optional. The number of strings in `argv` + \param[in] argv Optional. The command line arguments + Note that when running a Windows application (with WinMain()), the command line arguments will be retrieved and parsed even if argc and argv are nullptr + */ + static void run(const std::string& filename, IRenderer::UniquePtr& pRenderer, uint32_t argc = 0, char** argv = nullptr); + + virtual ~Sample(); + protected: + /************************************************************************/ + /* Callback inherited from ICallbacks */ + /************************************************************************/ + RenderContext* getRenderContext() override { return gpDevice ? gpDevice->getRenderContext() : nullptr; } + Fbo::SharedPtr getTargetFbo() override { return mpTargetFBO; } + Window* getWindow() override { return mpWindow.get(); } + Clock& getGlobalClock() override { return mClock; } + FrameRate& getFrameRate() override { return mFrameRate; } + void resizeSwapChain(uint32_t width, uint32_t height) override; + bool isKeyPressed(const KeyboardEvent::Key& key) override; + void toggleUI(bool showUI) override { mShowUI = showUI; } + bool isUiEnabled() override { return mShowUI; } + ArgList getArgList() override { return mArgList; } + void pauseRenderer(bool pause) override { mRendererPaused = pause; } + bool isRendererPaused() override { return mRendererPaused; } + std::string captureScreen(const std::string explicitFilename = "", const std::string explicitOutputDirectory = "") override; + void shutdown() override { if (mpWindow) { mpWindow->shutdown(); } } + SampleConfig getConfig() override; + void renderGlobalUI(Gui* pGui) override; + std::string getKeyboardShortcutsStr() override; + void toggleVsync(bool on) override { mVsyncOn = on; } + bool isVsyncEnabled() override { return mVsyncOn; } + + /** Internal data structures + */ + Gui::UniquePtr mpGui; ///< Main sample GUI + Fbo::SharedPtr mpTargetFBO; ///< The FBO available to renderers + bool mRendererPaused = false; ///< Freezes the renderer + ArgList mArgList; ///< Arguments passed in by command line + Window::SharedPtr mpWindow; ///< The application's window + + void renderFrame() override; + void handleWindowSizeChange() override; + void handleKeyboardEvent(const KeyboardEvent& keyEvent) override; + void handleMouseEvent(const MouseEvent& mouseEvent) override; + void handleDroppedFile(const std::string& filename) override; + + void initVideoCapture(); + + // Private functions + void initUI(); + void saveConfigToFile(); + + void startVideoCapture(); + void endVideoCapture(); + void captureVideoFrame(); + void renderUI(); + + void runInternal(const SampleConfig& config, uint32_t argc, char** argv); + + void startScripting(); + void registerScriptBindings(ScriptBindings::Module& m); + + bool mVsyncOn = false; + bool mShowUI = true; + bool mCaptureScreen = false; + FrameRate mFrameRate; + Clock mClock; + + IRenderer::UniquePtr mpRenderer; + + struct VideoCaptureData + { + VideoEncoderUI::UniquePtr pUI; + VideoEncoder::UniquePtr pVideoCapture; + std::vector pFrame; + double fixedTimeDelta = 0; + double currentTime = 0; + bool displayUI = false; + } mVideoCapture; + + std::set mPressedKeys; + PixelZoom::SharedPtr mpPixelZoom; + + Sample(IRenderer::UniquePtr& pRenderer) : mpRenderer(std::move(pRenderer)) {} + Sample(const Sample&) = delete; + Sample& operator=(const Sample&) = delete; + }; +}; diff --git a/Framework/Source/Graphics/ComputeState.cpp b/Source/Falcor/Core/State/ComputeState.cpp similarity index 91% rename from Framework/Source/Graphics/ComputeState.cpp rename to Source/Falcor/Core/State/ComputeState.cpp index 5f7d2cffb..dc66617b9 100644 --- a/Framework/Source/Graphics/ComputeState.cpp +++ b/Source/Falcor/Core/State/ComputeState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ComputeState.h" -#include "Graphics/Program/ProgramVars.h" +#include "Core/Program/ProgramVars.h" namespace Falcor { @@ -40,7 +40,7 @@ namespace Falcor ComputeStateObject::SharedPtr ComputeState::getCSO(const ComputeVars* pVars) { - ProgramVersion::SharedConstPtr pProgVersion = mpProgram ? mpProgram->getActiveVersion() : nullptr; + auto pProgVersion = mpProgram ? mpProgram->getActiveVersion() : nullptr; bool newProgram = (pProgVersion.get() != mCachedData.pProgramVersion); if (newProgram) { @@ -81,4 +81,9 @@ namespace Falcor return pCso; } -} \ No newline at end of file + + SCRIPT_BINDING(ComputeState) + { + m.regClass(ComputeState); + } +} diff --git a/Framework/Source/Graphics/ComputeState.h b/Source/Falcor/Core/State/ComputeState.h similarity index 92% rename from Framework/Source/Graphics/ComputeState.h rename to Source/Falcor/Core/State/ComputeState.h index 04224d58a..78e3270b2 100644 --- a/Framework/Source/Graphics/ComputeState.h +++ b/Source/Falcor/Core/State/ComputeState.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,10 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/ComputeStateObject.h" -#include "Graphics/Program/ComputeProgram.h" -#include -#include "Utils/Graph.h" +#include "StateGraph.h" +#include "Core/API/ComputeStateObject.h" +#include "Core/Program/ComputeProgram.h" namespace Falcor { @@ -39,7 +38,7 @@ namespace Falcor This class contains the entire state required by a single dispatch call. It's not an immutable object - you can change it dynamically during rendering. The recommended way to use it is to create multiple ComputeState objects (ideally, a single object per program) */ - class ComputeState + class dlldecl ComputeState { public: using SharedPtr = std::shared_ptr; @@ -78,7 +77,7 @@ namespace Falcor }; CachedData mCachedData; - using StateGraph = Graph; + using StateGraph = StateGraph; StateGraph::SharedPtr mpCsoGraph; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/GraphicsState.cpp b/Source/Falcor/Core/State/GraphicsState.cpp similarity index 97% rename from Framework/Source/Graphics/GraphicsState.cpp rename to Source/Falcor/Core/State/GraphicsState.cpp index 7cb190249..2e5ab55be 100644 --- a/Framework/Source/Graphics/GraphicsState.cpp +++ b/Source/Falcor/Core/State/GraphicsState.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "GraphicsState.h" -#include "Graphics/Program/ProgramVars.h" namespace Falcor { @@ -74,7 +73,7 @@ namespace Falcor { mpVao->getVertexLayout()->addVertexAttribDclToProg(mpProgram.get()); } - const ProgramVersion::SharedConstPtr pProgVersion = mpProgram ? mpProgram->getActiveVersion() : nullptr; + auto pProgVersion = mpProgram ? mpProgram->getActiveVersion() : nullptr; bool newProgVersion = pProgVersion.get() != mCachedData.pProgramVersion; if (newProgVersion) { @@ -153,7 +152,7 @@ namespace Falcor { if (mFboStack.empty()) { - logError("PipelineState::popFbo() - can't pop FBO since the viewport stack is empty."); + logError("PipelineState::popFbo() - can't pop FBO since the FBO stack is empty."); return; } setFbo(mFboStack.top(), setVp0Sc0); @@ -283,4 +282,9 @@ namespace Falcor } #endif } -} \ No newline at end of file + + SCRIPT_BINDING(GraphicsState) + { + m.regClass(GraphicsState); + } +} diff --git a/Framework/Source/Graphics/GraphicsState.h b/Source/Falcor/Core/State/GraphicsState.h similarity index 96% rename from Framework/Source/Graphics/GraphicsState.h rename to Source/Falcor/Core/State/GraphicsState.h index 6ddddaebd..53c7b075b 100644 --- a/Framework/Source/Graphics/GraphicsState.h +++ b/Source/Falcor/Core/State/GraphicsState.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,25 +26,23 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/GraphicsStateObject.h" -#include "Graphics/Program/GraphicsProgram.h" -#include "API/VAO.h" -#include "API/FBO.h" -#include "API/RasterizerState.h" -#include "API/DepthStencilState.h" -#include "API/BlendState.h" -#include -#include "Utils/Graph.h" +#include "Core/API/GraphicsStateObject.h" +#include "StateGraph.h" +#include "Core/API/FBO.h" +#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/ProgramVars.h" namespace Falcor { class GraphicsVars; + class Vao; + class GraphicsProgram; /** Pipeline state. This class contains the entire state required by a single draw-call. It's not an immutable object - you can change it dynamically during rendering. The recommended way to use it is to create multiple PipelineState objects (ideally, a single object per render-pass) */ - class GraphicsState + class dlldecl GraphicsState { public: using SharedPtr = std::shared_ptr; @@ -263,7 +261,7 @@ namespace Falcor }; CachedData mCachedData; - using StateGraph = Graph; + using StateGraph = StateGraph; StateGraph::SharedPtr mpGsoGraph; }; } diff --git a/Framework/Source/Utils/Graph.h b/Source/Falcor/Core/State/StateGraph.h similarity index 93% rename from Framework/Source/Utils/Graph.h rename to Source/Falcor/Core/State/StateGraph.h index dfed5a030..6eaede797 100644 --- a/Framework/Source/Utils/Graph.h +++ b/Source/Falcor/Core/State/StateGraph.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,17 +31,17 @@ namespace Falcor { template> - class Graph + class StateGraph { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; using CompareFunc = std::function; static SharedPtr create() { - return SharedPtr(new Graph()); + return SharedPtr(new StateGraph()); } bool isEdgeExists(const EdgeType& e) const @@ -105,7 +105,7 @@ namespace Falcor } private: - Graph() : mGraph(1) {} + StateGraph() : mGraph(1) {} using edge_map = std::unordered_map; @@ -124,4 +124,4 @@ namespace Falcor std::vector mGraph; uint32_t mCurrentNode = 0; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/Window.cpp b/Source/Falcor/Core/Window.cpp similarity index 92% rename from Framework/Source/API/Window.cpp rename to Source/Falcor/Core/Window.cpp index d75026c29..dde274839 100644 --- a/Framework/Source/API/Window.cpp +++ b/Source/Falcor/Core/Window.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "API/Window.h" -#include "Utils/UserInput.h" -#include "Utils/Platform/OS.h" -#include -#include "API/Texture.h" -#include "API/FBO.h" -#include "Utils/StringUtils.h" +#include "stdafx.h" +#include "Core/Window.h" +#include "Utils/UI/UserInput.h" // Don't include GL/GLES headers #define GLFW_INCLUDE_NONE @@ -109,6 +104,7 @@ namespace Falcor MouseEvent event; event.type = MouseEvent::Type::Move; event.pos = calcMousePos(mouseX, mouseY, pWindow->getMouseScale()); + event.screenPos = { mouseX, mouseY }; event.wheelDelta = glm::vec2(0, 0); pWindow->mpCallbacks->handleMouseEvent(event); @@ -345,11 +341,10 @@ namespace Falcor } }; - Window::Window(ICallbacks* pCallbacks, uint32_t width, uint32_t height) + Window::Window(ICallbacks* pCallbacks, const Desc& desc) : mpCallbacks(pCallbacks) - , mWidth(width) - , mHeight(height) - , mMouseScale(1.0f / (float)width, 1.0f / (float)height) + , mDesc(desc) + , mMouseScale(1.0f / (float)desc.width, 1.0f / (float)desc.height) { } @@ -359,10 +354,10 @@ namespace Falcor int32_t actualWidth, actualHeight; glfwGetWindowSize(mpGLFWWindow, &actualWidth, &actualHeight); - mWidth = uint32_t(actualWidth); - mHeight = uint32_t(actualHeight); - mMouseScale.x = 1.0f / (float)mWidth; - mMouseScale.y = 1.0f / (float)mHeight; + mDesc.width = uint32_t(actualWidth); + mDesc.height = uint32_t(actualHeight); + mMouseScale.x = 1.0f / (float)mDesc.width; + mMouseScale.y = 1.0f / (float)mDesc.height; } Window::~Window() @@ -388,13 +383,24 @@ namespace Falcor return nullptr; } - SharedPtr pWindow = SharedPtr(new Window(pCallbacks, desc.width, desc.height)); + SharedPtr pWindow = SharedPtr(new Window(pCallbacks, desc)); // Create the window glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - GLFWmonitor* mon = desc.fullScreen ? glfwGetPrimaryMonitor() : nullptr; - GLFWwindow* pGLFWWindow = glfwCreateWindow(desc.width, desc.height, desc.title.c_str(), mon, nullptr); + uint32_t w = desc.width; + uint32_t h = desc.height; + if (desc.fullScreen) + { + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + auto mon = glfwGetPrimaryMonitor(); + auto mod = glfwGetVideoMode(mon); + w = mod->width; + h = mod->height; + } + + GLFWwindow* pGLFWWindow = glfwCreateWindow(w, h, desc.title.c_str(), nullptr, nullptr); + if (pGLFWWindow == nullptr) { logError("Window creation failed!"); @@ -460,4 +466,12 @@ namespace Falcor { glfwPollEvents(); } + + SCRIPT_BINDING(Window) + { + auto winDesc = m.class_("WindowDesc"); +#define desc_field(f_) rwField(#f_, &Window::Desc::f_) + winDesc.desc_field(width).desc_field(height).desc_field(fullScreen).desc_field(title).desc_field(resizableWindow); +#undef desc_field + } } diff --git a/Framework/Source/API/Window.h b/Source/Falcor/Core/Window.h similarity index 87% rename from Framework/Source/API/Window.h rename to Source/Falcor/Core/Window.h index 558836131..f5c02b874 100644 --- a/Framework/Source/API/Window.h +++ b/Source/Falcor/Core/Window.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,14 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Utils/UserInput.h" -#include - struct GLFWwindow; namespace Falcor { - class Window + struct KeyboardEvent; + struct MouseEvent; + + class dlldecl Window { public: using SharedPtr = std::shared_ptr; @@ -42,15 +42,14 @@ namespace Falcor /** Window configuration configuration */ - struct Desc + struct Desc : ScriptBindings::enable_to_string { uint32_t width = 1920; ///< The width of the client area size uint32_t height = 1080; ///< The height of the client area size - bool fullScreen = false; ///< Set to true to run the sample in full-screen mode + bool fullScreen = false; ///< Set to true to run the sample in full-screen mode. Width and height will be ignored std::string title = "Falcor Sample"; ///< Window title bool resizableWindow = true; ///< Allow the user to resize the window. - bool acceptDropFiles = false; ///< Allow the user to drag-and-drop files into the window - }; + }; /** Callbacks interface to be used when creating a new object */ @@ -104,24 +103,22 @@ namespace Falcor /** Get the width of the window's client area */ - uint32_t getClientAreaWidth() const { return mWidth; } + uvec2 getClientAreaSize() const { return { mDesc.width, mDesc.height }; } - /** Get the height of the window's client area + /** Get the descriptor */ - uint32_t getClientAreaHeight() const { return mHeight; } - + const Desc& getDesc() const { return mDesc; } private: friend class ApiCallbacks; - Window(ICallbacks* pCallbacks, uint32_t width, uint32_t height); + Window(ICallbacks* pCallbacks, const Desc& desc); void checkWindowSize(); + Desc mDesc; GLFWwindow* mpGLFWWindow; ApiHandle mApiHandle; - uint32_t mWidth; - uint32_t mHeight; glm::vec2 mMouseScale; const glm::vec2& getMouseScale() const { return mMouseScale; } ICallbacks* mpCallbacks = nullptr; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Data/ApplyAO.ps.slang b/Source/Falcor/Data/ApplyAO.ps.slang similarity index 96% rename from Framework/Source/Data/ApplyAO.ps.slang rename to Source/Falcor/Data/ApplyAO.ps.slang index fc77fa35e..8d4628336 100644 --- a/Framework/Source/Data/ApplyAO.ps.slang +++ b/Source/Falcor/Data/ApplyAO.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/CascadedShadowMap.slang b/Source/Falcor/Data/Effects/CascadedShadowMap.slang similarity index 99% rename from Framework/Source/Data/Effects/CascadedShadowMap.slang rename to Source/Falcor/Data/Effects/CascadedShadowMap.slang index a53f9c0c3..71f9d6263 100644 --- a/Framework/Source/Data/Effects/CascadedShadowMap.slang +++ b/Source/Falcor/Data/Effects/CascadedShadowMap.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/CsmData.h b/Source/Falcor/Data/Effects/CsmData.h similarity index 96% rename from Framework/Source/Data/Effects/CsmData.h rename to Source/Falcor/Data/Effects/CsmData.h index 11bc08116..2b268f365 100644 --- a/Framework/Source/Data/Effects/CsmData.h +++ b/Source/Falcor/Data/Effects/CsmData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -40,6 +40,8 @@ #define CsmFilterEvsm4 5 #define CsmFilterStochasticPcf 6 +BEGIN_NAMESPACE_FALCOR + struct CsmData { float4x4 globalMat; @@ -70,4 +72,6 @@ struct CsmData #ifdef HOST_CODE static_assert(sizeof(CsmData) % sizeof(float4) == 0, "CsmData size should be aligned on float4 size"); #endif +END_NAMESPACE_FALCOR + #endif //CSMDATA_H diff --git a/Framework/Source/Data/Effects/DepthPass.slang b/Source/Falcor/Data/Effects/DepthPass.slang similarity index 89% rename from Framework/Source/Data/Effects/DepthPass.slang rename to Source/Falcor/Data/Effects/DepthPass.slang index c66c96f47..7073b0601 100644 --- a/Framework/Source/Data/Effects/DepthPass.slang +++ b/Source/Falcor/Data/Effects/DepthPass.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,9 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import DefaultVS; +__import Raster; __import ShaderCommon; __import Effects.CascadedShadowMap; +__import Scene; layout(binding = 0) SamplerState alphaSampler : register(s0); layout(binding = 1) texture2D alphaMap : register(t0); @@ -52,20 +53,16 @@ struct ShadowPassVSOut #endif }; -ShadowPassVSOut vsMain(VertexIn vIn) +ShadowPassVSOut vsMain(VSIn vIn) { ShadowPassVSOut vOut; - float4x4 worldMat = getWorldMat(vIn); + float4x4 worldMat = gScene.getWorldMatrix(vIn.meshInstanceID); vOut.pos = mul(vIn.pos, worldMat); #ifdef _APPLY_PROJECTION - vOut.pos = mul(vOut.pos, gCamera.viewProjMat); + vOut.pos = mul(vOut.pos, gScene.camera.viewProjMat); #endif -#ifdef HAS_TEXCRD vOut.texC = vIn.texC; -#else - vOut.texC = float2(0.5f, 0.5f); -#endif return vOut; } diff --git a/Framework/Source/Data/Effects/FXAA.slang b/Source/Falcor/Data/Effects/FXAA.slang similarity index 99% rename from Framework/Source/Data/Effects/FXAA.slang rename to Source/Falcor/Data/Effects/FXAA.slang index 6e7e19541..501946284 100644 --- a/Framework/Source/Data/Effects/FXAA.slang +++ b/Source/Falcor/Data/Effects/FXAA.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/GaussianBlur.ps.slang b/Source/Falcor/Data/Effects/GaussianBlur.ps.slang similarity index 95% rename from Framework/Source/Data/Effects/GaussianBlur.ps.slang rename to Source/Falcor/Data/Effects/GaussianBlur.ps.slang index f46de8de3..5e708e6ec 100644 --- a/Framework/Source/Data/Effects/GaussianBlur.ps.slang +++ b/Source/Falcor/Data/Effects/GaussianBlur.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -55,7 +55,7 @@ float4 blur(float2 texC) #elif defined _VERTICAL_BLUR const float2 dir = float2(0, 1); #else - Error. Need to define either _HORIZONTAL_BLUR or _VERTICAL_BLUR +#error Please define either _HORIZONTAL_BLUR or _VERTICAL_BLUR #endif const int2 offset = -(_KERNEL_WIDTH / 2) * dir; @@ -82,4 +82,4 @@ float4 main(BlurPSIn pIn) : SV_TARGET0 fragColor = blur(pIn.texC); #endif return fragColor; -} \ No newline at end of file +} diff --git a/Framework/Source/Data/Effects/LeanMapping.slang b/Source/Falcor/Data/Effects/LeanMapping.slang similarity index 96% rename from Framework/Source/Data/Effects/LeanMapping.slang rename to Source/Falcor/Data/Effects/LeanMapping.slang index a26dac6a3..2c3f4ee16 100644 --- a/Framework/Source/Data/Effects/LeanMapping.slang +++ b/Source/Falcor/Data/Effects/LeanMapping.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleConstColor.ps.slang b/Source/Falcor/Data/Effects/ParticleConstColor.ps.slang similarity index 96% rename from Framework/Source/Data/Effects/ParticleConstColor.ps.slang rename to Source/Falcor/Data/Effects/ParticleConstColor.ps.slang index d8d50dbd0..6b9e90a21 100644 --- a/Framework/Source/Data/Effects/ParticleConstColor.ps.slang +++ b/Source/Falcor/Data/Effects/ParticleConstColor.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleData.h b/Source/Falcor/Data/Effects/ParticleData.h similarity index 96% rename from Framework/Source/Data/Effects/ParticleData.h rename to Source/Falcor/Data/Effects/ParticleData.h index 1cba1eaa0..8fd4362f0 100644 --- a/Framework/Source/Data/Effects/ParticleData.h +++ b/Source/Falcor/Data/Effects/ParticleData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,6 +32,7 @@ #define EMIT_THREADS 64 #define SORT_THREADS 1024 +BEGIN_NAMESPACE_FALCOR struct Particle { @@ -94,5 +95,6 @@ uint getParticleIndex(uint groupIDx, uint threadsPerGroup, uint groupIndex) return groupIDx * threadsPerGroup + groupIndex; } #endif +END_NAMESPACE_FALCOR #endif //particleData_h diff --git a/Framework/Source/Data/Effects/ParticleEmit.cs.slang b/Source/Falcor/Data/Effects/ParticleEmit.cs.slang similarity index 97% rename from Framework/Source/Data/Effects/ParticleEmit.cs.slang rename to Source/Falcor/Data/Effects/ParticleEmit.cs.slang index 0256de279..9a9bd65eb 100644 --- a/Framework/Source/Data/Effects/ParticleEmit.cs.slang +++ b/Source/Falcor/Data/Effects/ParticleEmit.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleInterpColor.ps.slang b/Source/Falcor/Data/Effects/ParticleInterpColor.ps.slang similarity index 97% rename from Framework/Source/Data/Effects/ParticleInterpColor.ps.slang rename to Source/Falcor/Data/Effects/ParticleInterpColor.ps.slang index 6741ddc56..200956df9 100644 --- a/Framework/Source/Data/Effects/ParticleInterpColor.ps.slang +++ b/Source/Falcor/Data/Effects/ParticleInterpColor.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleSimulate.cs.slang b/Source/Falcor/Data/Effects/ParticleSimulate.cs.slang similarity index 98% rename from Framework/Source/Data/Effects/ParticleSimulate.cs.slang rename to Source/Falcor/Data/Effects/ParticleSimulate.cs.slang index 4a86cbe17..8e0d7a758 100644 --- a/Framework/Source/Data/Effects/ParticleSimulate.cs.slang +++ b/Source/Falcor/Data/Effects/ParticleSimulate.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleSort.cs.slang b/Source/Falcor/Data/Effects/ParticleSort.cs.slang similarity index 98% rename from Framework/Source/Data/Effects/ParticleSort.cs.slang rename to Source/Falcor/Data/Effects/ParticleSort.cs.slang index f01760413..5699e0de1 100644 --- a/Framework/Source/Data/Effects/ParticleSort.cs.slang +++ b/Source/Falcor/Data/Effects/ParticleSort.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleTexture.ps.slang b/Source/Falcor/Data/Effects/ParticleTexture.ps.slang similarity index 97% rename from Framework/Source/Data/Effects/ParticleTexture.ps.slang rename to Source/Falcor/Data/Effects/ParticleTexture.ps.slang index efe54a831..9b01b2483 100644 --- a/Framework/Source/Data/Effects/ParticleTexture.ps.slang +++ b/Source/Falcor/Data/Effects/ParticleTexture.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ParticleVertex.vs.slang b/Source/Falcor/Data/Effects/ParticleVertex.vs.slang similarity index 98% rename from Framework/Source/Data/Effects/ParticleVertex.vs.slang rename to Source/Falcor/Data/Effects/ParticleVertex.vs.slang index ba35733cb..37d4eaadf 100644 --- a/Framework/Source/Data/Effects/ParticleVertex.vs.slang +++ b/Source/Falcor/Data/Effects/ParticleVertex.vs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/SSAO.ps.slang b/Source/Falcor/Data/Effects/SSAO.ps.slang similarity index 89% rename from Framework/Source/Data/Effects/SSAO.ps.slang rename to Source/Falcor/Data/Effects/SSAO.ps.slang index de71690a9..529a4d356 100644 --- a/Framework/Source/Data/Effects/SSAO.ps.slang +++ b/Source/Falcor/Data/Effects/SSAO.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ __import ShaderCommon; +__import Scene; #include "SSAOData.h" cbuffer SSAOCB : register(b0) @@ -52,7 +53,7 @@ float4 getPosition(float2 uv) pos.z = gDepthTex.SampleLevel(gTextureSampler, uv, 0).r; pos.w = 1.0f; - float4 posW = mul(pos, gCamera.invViewProj); + float4 posW = mul(pos, gScene.camera.invViewProj); posW /= posW.w; return posW; @@ -68,7 +69,7 @@ float4 main(float2 texC : TEXCOORD) : SV_TARGET0 // Calculate world position of pixel float3 posW = getPosition(texC).xyz; float3 normal = normalize(gNormalTex.Sample(gTextureSampler, texC).xyz * 2.0f - 1.0f); - float originDist = length(posW - gCamera.posW); + float originDist = length(posW - gScene.camera.posW); float3 randDir = gNoiseTex.Sample(gNoiseSampler, texC * gData.noiseScale).xyz * 2.0f - 1.0f; float3 tangent = normalize(randDir - normal * dot(randDir, normal)); @@ -83,10 +84,10 @@ float4 main(float2 texC : TEXCOORD) : SV_TARGET0 // Calculate sample world space pos float3 samplePosW = posW + (kernelPos * gData.radius); - float sampleDepth = length(samplePosW - gCamera.posW); + float sampleDepth = length(samplePosW - gScene.camera.posW); // Get screen space pos of sample - float4 samplePosProj = mul(float4(samplePosW, 1.0f), gCamera.viewProjMat); + float4 samplePosProj = mul(float4(samplePosW, 1.0f), gScene.camera.viewProjMat); samplePosProj /= samplePosProj.w; // Sample depth buffer at the same place as sample @@ -95,7 +96,7 @@ float4 main(float2 texC : TEXCOORD) : SV_TARGET0 samplePosProj.y = -samplePosProj.y; #endif float2 sampleUV = saturate(float2(samplePosProj.x, -samplePosProj.y) * 0.5f + 0.5f); - float sceneDepth = length(getPosition(sampleUV).xyz - gCamera.posW); + float sceneDepth = length(getPosition(sampleUV).xyz - gScene.camera.posW); float rangeCheck = step(abs(sampleDepth - sceneDepth), gData.radius); occlusion += step(sceneDepth, sampleDepth) * rangeCheck; diff --git a/Framework/Source/Data/Effects/SSAOData.h b/Source/Falcor/Data/Effects/SSAOData.h similarity index 92% rename from Framework/Source/Data/Effects/SSAOData.h rename to Source/Falcor/Data/Effects/SSAOData.h index 13314a6c0..eec33565d 100644 --- a/Framework/Source/Data/Effects/SSAOData.h +++ b/Source/Falcor/Data/Effects/SSAOData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,13 +31,16 @@ #include "Data/HostDeviceData.h" #define MAX_SAMPLES 32 +BEGIN_NAMESPACE_FALCOR struct SSAOData { float4 sampleKernel[MAX_SAMPLES]; float2 noiseScale DEFAULTS(float2(1, 1)); - uint32_t kernelSize DEFAULTS(1); + uint32_t kernelSize DEFAULTS(16); float radius DEFAULTS(0.1f); }; +END_NAMESPACE_FALCOR + #endif //SSAODATA_H diff --git a/Framework/Source/Data/Effects/ShadowPass.slang b/Source/Falcor/Data/Effects/ShadowPass.slang similarity index 93% rename from Framework/Source/Data/Effects/ShadowPass.slang rename to Source/Falcor/Data/Effects/ShadowPass.slang index 6e104532d..6fece1dda 100644 --- a/Framework/Source/Data/Effects/ShadowPass.slang +++ b/Source/Falcor/Data/Effects/ShadowPass.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,9 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include "VertexAttrib.h" -__import DefaultVS; +__import Raster; __import ShaderCommon; __import Effects.CascadedShadowMap; +__import Scene; layout(binding = 0) SamplerState alphaSampler : register(s0); layout(binding = 1) texture2D alphaMap : register(t0); @@ -60,20 +61,16 @@ struct ShadowPassVSOut #endif }; -ShadowPassVSOut vsMain(VertexIn vIn) +ShadowPassVSOut vsMain(VSIn vIn) { ShadowPassVSOut vOut; - float4x4 worldMat = getWorldMat(vIn); + float4x4 worldMat = gScene.getWorldMatrix(vIn.meshInstanceID); vOut.pos = mul(vIn.pos, worldMat); #ifdef _APPLY_PROJECTION - vOut.pos = mul(vOut.pos, gCamera.viewProjMat); + vOut.pos = mul(vOut.pos, gScene.camera.viewProjMat); #endif -#ifdef HAS_TEXCRD vOut.texC = vIn.texC; -#else - vOut.texC = float2(0.5f, 0.5f); -#endif return vOut; } diff --git a/Framework/Source/Data/Effects/SkyBox.slang b/Source/Falcor/Data/Effects/SkyBox.slang similarity index 91% rename from Framework/Source/Data/Effects/SkyBox.slang rename to Source/Falcor/Data/Effects/SkyBox.slang index e4f4e1fd6..a6b623a5d 100644 --- a/Framework/Source/Data/Effects/SkyBox.slang +++ b/Source/Falcor/Data/Effects/SkyBox.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,6 +31,7 @@ fixme __import ShaderCommon; __import Helpers; +__import Scene; #ifdef _SPHERICAL_MAP Texture2D gTexture; @@ -42,14 +43,16 @@ SamplerState gSampler; cbuffer PerFrameCB : register(b0) { float4x4 gWorld; + float4x4 gViewMat; + float4x4 gProjMat; float gScale; }; void vs(float4 posL : POSITION, out float3 dir : NORMAL, out float4 posH : SV_POSITION) { dir = posL.xyz; - float4 viewPos = mul(mul(posL, gWorld), gCamera.viewMat); - posH = mul(viewPos, gCamera.projMat); + float4 viewPos = mul(mul(posL, gWorld), gViewMat); + posH = mul(viewPos, gProjMat); posH.xy *= gScale; posH.z = posH.w; diff --git a/Framework/Source/Data/Effects/TAA.ps.slang b/Source/Falcor/Data/Effects/TAA.ps.slang similarity index 98% rename from Framework/Source/Data/Effects/TAA.ps.slang rename to Source/Falcor/Data/Effects/TAA.ps.slang index 8be2e7984..2582ed5e8 100644 --- a/Framework/Source/Data/Effects/TAA.ps.slang +++ b/Source/Falcor/Data/Effects/TAA.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/ToneMapping.ps.slang b/Source/Falcor/Data/Effects/ToneMapping.ps.slang similarity index 82% rename from Framework/Source/Data/Effects/ToneMapping.ps.slang rename to Source/Falcor/Data/Effects/ToneMapping.ps.slang index b426635a1..5c491d501 100644 --- a/Framework/Source/Data/Effects/ToneMapping.ps.slang +++ b/Source/Falcor/Data/Effects/ToneMapping.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,6 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "ToneMappingData.h" SamplerState gLuminanceTexSampler : register(s0); SamplerState gColorSampler : register(s1); @@ -34,10 +35,7 @@ Texture2D gLuminanceTex; cbuffer PerImageCB : register(b0) { - float gExposureKey; - float gMaxWhiteLuminance; - float gLuminanceLod; - float gWhiteScale; + ToneMappingData gToneMappingData; }; float calcLuminance(float3 color) @@ -48,9 +46,9 @@ float calcLuminance(float3 color) float3 calcExposedColor(float3 color, float2 texC) { float pixelLuminance = calcLuminance(color); - float avgLuminance = gLuminanceTex.SampleLevel(gLuminanceTexSampler, texC, gLuminanceLod).r; + float avgLuminance = gLuminanceTex.SampleLevel(gLuminanceTexSampler, texC, gToneMappingData.luminanceLod).r; avgLuminance = exp2(avgLuminance); - float exposedLuminance = (gExposureKey / avgLuminance); + float exposedLuminance = (gToneMappingData.exposureKey / avgLuminance); return exposedLuminance*color; } @@ -71,7 +69,7 @@ float3 reinhardToneMap(float3 color) float3 reinhardModifiedToneMap(float3 color) { float luminance = calcLuminance(color); - float reinhard = luminance * (1 + luminance / (gMaxWhiteLuminance * gMaxWhiteLuminance)) * (1 + luminance); + float reinhard = luminance * (1 + luminance / (gToneMappingData.whiteMaxLuminance * gToneMappingData.whiteMaxLuminance)) * (1 + luminance); return color * (reinhard / luminance); } @@ -105,7 +103,7 @@ float3 Uc2ToneMap(float3 color) { float exposureBias = 2.0f; color = ApplyUc2Curve(exposureBias * color); - float whiteScale = 1 / ApplyUc2Curve(gWhiteScale.xxx).x; + float whiteScale = 1 / ApplyUc2Curve(gToneMappingData.whiteScale.xxx).x; color = color * whiteScale; return color; @@ -123,6 +121,22 @@ float3 AcesToneMap(float3 color) return color; } +float3 photoToneMap(float3 color) +{ + color = mul(color, (float3x3) gToneMappingData.finalTransform); + color = max(color, 0); // Clamp to avoid negative out-of-gamut colors + + if (gToneMappingData.applyAcesCurve) + { + // Cancel out the pre-exposure mentioned in + // https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ + color *= 0.6f; + color = AcesToneMap(color); + } + + return saturate(color); +} + float4 calcColor(float2 texC) { float4 color = gColorTex.Sample(gColorSampler, texC); @@ -145,6 +159,8 @@ float4 calcColor(float2 texC) return float4(Uc2ToneMap(exposedColor), color.a); #elif defined (_ACES) return float4(AcesToneMap(exposedColor), color.a); +#elif defined (_PHOTO) + return float4(photoToneMap(color.rgb), color.a); #endif return float4(exposedColor, color.a); } diff --git a/Source/Falcor/Data/Effects/ToneMappingData.h b/Source/Falcor/Data/Effects/ToneMappingData.h new file mode 100644 index 000000000..44fc14c62 --- /dev/null +++ b/Source/Falcor/Data/Effects/ToneMappingData.h @@ -0,0 +1,47 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "../HostDeviceData.h" + +BEGIN_NAMESPACE_FALCOR + +struct ToneMappingData +{ + float exposureKey = 0.042f; + float whiteMaxLuminance = 1.0f; + float luminanceLod = 16; // Max possible LOD, will result in global operation + float whiteScale = 11.2f; + + // Photographic exposure + uint applyAcesCurve = 1u; // Apply ACES filmic tone mapping curve (approx). + float3 _padding; + + float3x4 finalTransform; // Final transform in RGB space with exposure scaling baked in (we only use the 3x3 part). +}; + +END_NAMESPACE_FALCOR diff --git a/Framework/Source/Data/Effects/VisibilityPass.ps.slang b/Source/Falcor/Data/Effects/VisibilityPass.ps.slang similarity index 97% rename from Framework/Source/Data/Effects/VisibilityPass.ps.slang rename to Source/Falcor/Data/Effects/VisibilityPass.ps.slang index 80cceaf60..044b6e03b 100644 --- a/Framework/Source/Data/Effects/VisibilityPass.ps.slang +++ b/Source/Falcor/Data/Effects/VisibilityPass.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Effects/cube.obj b/Source/Falcor/Data/Effects/cube.obj similarity index 100% rename from Framework/Source/Data/Effects/cube.obj rename to Source/Falcor/Data/Effects/cube.obj diff --git a/Framework/Source/Data/Framework/Fonts/DejaVu Sans Mono14.000000.bin b/Source/Falcor/Data/Framework/Fonts/DejaVu Sans Mono14.000000.bin similarity index 100% rename from Framework/Source/Data/Framework/Fonts/DejaVu Sans Mono14.000000.bin rename to Source/Falcor/Data/Framework/Fonts/DejaVu Sans Mono14.000000.bin diff --git a/Framework/Source/Data/Framework/Fonts/DejaVu Sans Mono14.000000.dds b/Source/Falcor/Data/Framework/Fonts/DejaVu Sans Mono14.000000.dds similarity index 100% rename from Framework/Source/Data/Framework/Fonts/DejaVu Sans Mono14.000000.dds rename to Source/Falcor/Data/Framework/Fonts/DejaVu Sans Mono14.000000.dds diff --git a/Framework/Source/Data/Framework/Fonts/consolab.ttf b/Source/Falcor/Data/Framework/Fonts/consolab.ttf similarity index 100% rename from Framework/Source/Data/Framework/Fonts/consolab.ttf rename to Source/Falcor/Data/Framework/Fonts/consolab.ttf diff --git a/Framework/Source/Data/Framework/Fonts/trebucbd.ttf b/Source/Falcor/Data/Framework/Fonts/trebucbd.ttf similarity index 100% rename from Framework/Source/Data/Framework/Fonts/trebucbd.ttf rename to Source/Falcor/Data/Framework/Fonts/trebucbd.ttf diff --git a/Framework/Source/Data/Framework/Models/Camera.obj b/Source/Falcor/Data/Framework/Models/Camera.obj similarity index 100% rename from Framework/Source/Data/Framework/Models/Camera.obj rename to Source/Falcor/Data/Framework/Models/Camera.obj diff --git a/Framework/Source/Data/Framework/Models/LightBulb.obj b/Source/Falcor/Data/Framework/Models/LightBulb.obj similarity index 100% rename from Framework/Source/Data/Framework/Models/LightBulb.obj rename to Source/Falcor/Data/Framework/Models/LightBulb.obj diff --git a/Framework/Source/Data/Framework/Models/RotateGizmo.obj b/Source/Falcor/Data/Framework/Models/RotateGizmo.obj similarity index 100% rename from Framework/Source/Data/Framework/Models/RotateGizmo.obj rename to Source/Falcor/Data/Framework/Models/RotateGizmo.obj diff --git a/Framework/Source/Data/Framework/Models/ScaleGizmo.obj b/Source/Falcor/Data/Framework/Models/ScaleGizmo.obj similarity index 100% rename from Framework/Source/Data/Framework/Models/ScaleGizmo.obj rename to Source/Falcor/Data/Framework/Models/ScaleGizmo.obj diff --git a/Framework/Source/Data/Framework/Models/TranslateGizmo.obj b/Source/Falcor/Data/Framework/Models/TranslateGizmo.obj similarity index 100% rename from Framework/Source/Data/Framework/Models/TranslateGizmo.obj rename to Source/Falcor/Data/Framework/Models/TranslateGizmo.obj diff --git a/Framework/Source/Data/Framework/Nvidia.ico b/Source/Falcor/Data/Framework/Nvidia.ico similarity index 100% rename from Framework/Source/Data/Framework/Nvidia.ico rename to Source/Falcor/Data/Framework/Nvidia.ico diff --git a/Framework/Source/Data/Framework/Shaders/Blit.ps.slang b/Source/Falcor/Data/Framework/Shaders/Blit.slang similarity index 83% rename from Framework/Source/Data/Framework/Shaders/Blit.ps.slang rename to Source/Falcor/Data/Framework/Shaders/Blit.slang index b4fabf57a..7f720fe12 100644 --- a/Framework/Source/Data/Framework/Shaders/Blit.ps.slang +++ b/Source/Falcor/Data/Framework/Shaders/Blit.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,6 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +cbuffer SrcRectCB +{ + float2 gOffset; + float2 gScale; +} + + #ifndef SAMPLE_COUNT Texture2D gTex; #else @@ -32,7 +39,21 @@ Texture2DMS gTex; #endif SamplerState gSampler; -float4 main(float2 texC : TEXCOORD) : SV_TARGET +struct VsOut +{ + float2 texC : TEXCOORD; + float4 posH : SV_POSITION; +}; + +VsOut vs(float4 posS : POSITION, float2 texC : TEXCOORD) +{ + VsOut vOut; + vOut.texC = texC * gScale + gOffset; + vOut.posH = posS; + return vOut; +} + +float4 ps(float2 texC : TEXCOORD) : SV_TARGET { #ifndef SAMPLE_COUNT return gTex.Sample(gSampler, texC); @@ -51,4 +72,4 @@ float4 main(float2 texC : TEXCOORD) : SV_TARGET c /= SAMPLE_COUNT; return c; #endif -} \ No newline at end of file +} diff --git a/Framework/Source/Data/Framework/Shaders/ComputeSkinning.cs.slang b/Source/Falcor/Data/Framework/Shaders/ComputeSkinning.cs.slang similarity index 87% rename from Framework/Source/Data/Framework/Shaders/ComputeSkinning.cs.slang rename to Source/Falcor/Data/Framework/Shaders/ComputeSkinning.cs.slang index d047f63b2..6ce9f3a76 100644 --- a/Framework/Source/Data/Framework/Shaders/ComputeSkinning.cs.slang +++ b/Source/Falcor/Data/Framework/Shaders/ComputeSkinning.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,11 +28,8 @@ #include "Data/HostDeviceData.h" // For MAX_INSTANCES_BONES define -cbuffer PerModelCB -{ - float4x4 gBoneMat[MAX_BONES]; // Per-model bone matrices - float4x4 gInvTransposeBoneMat[MAX_BONES]; // Per-model bone inverse transpose matrices -}; +StructuredBuffer gBoneMat; // Per-model bone matrices +StructuredBuffer gInvTransposeBoneMat; // Per-model bone inverse transpose matrices cbuffer PerMeshCB { @@ -56,19 +53,18 @@ struct Vertex { float3 pos; float3 prevPos; -#ifdef HAS_NORMAL float3 normal; -#endif -#ifdef HAS_BITANGENT float3 bitangent; -#endif float4 boneWeights; uint4 boneIds; }; -uint4 unpackUint4x8(uint packedInput) +uint4 unpackUint4x16(uint2 packedInput) { - return uint4(packedInput, packedInput >> 8, packedInput >> 16, packedInput >> 24) & 0xff; + uint4 ret; + ret.xy = uint2(packedInput.x, packedInput.x >> 16) & 0xff; + ret.zw = uint2(packedInput.y, packedInput.y >> 16) & 0xff; + return ret; } Vertex loadVertexAttributes(uint vertexIndex) @@ -81,16 +77,12 @@ Vertex loadVertexAttributes(uint vertexIndex) #else v.prevPos = asfloat(gSkinnedPositions.Load3((vertexIndex * 3) * 4)); // Load previously skinned position #endif -#ifdef HAS_NORMAL v.normal = asfloat(gNormals.Load3((vertexIndex * 3) * 4)); -#endif -#ifdef HAS_BITANGENT v.bitangent = asfloat(gBitangents.Load3((vertexIndex * 3) * 4)); -#endif // Load bone weights and IDs v.boneWeights = asfloat(gBoneWeights.Load4((vertexIndex * 4) * 4)); // RGBA32Float - v.boneIds = unpackUint4x8(gBoneIds.Load(vertexIndex * 4)); // RGBA8Uint + v.boneIds = unpackUint4x16(gBoneIds.Load2(vertexIndex * 4 * 2)); return v; } @@ -99,10 +91,7 @@ void storeVertexAttributes(uint vertexIndex, Vertex v) { gSkinnedPositions.Store3((vertexIndex * 3) * 4, asuint(v.pos)); gSkinnedPrevPositions.Store3((vertexIndex * 3) * 4, asuint(v.prevPos)); -#ifdef HAS_NORMAL gSkinnedNormals.Store3((vertexIndex * 3) * 4, asuint(v.normal)); -#endif -#ifdef HAS_BITANGENT gSkinnedBitangents.Store3((vertexIndex * 3) * 4, asuint(v.bitangent)); #endif } @@ -146,12 +135,7 @@ void main(uint3 dispatchThreadID : SV_DispatchThreadID) #else vOut.prevPos = vIn.prevPos; #endif -#ifdef HAS_NORMAL vOut.normal = mul(vIn.normal, invTransposeBoneMat).xyz; -#endif -#ifdef HAS_BITANGENT vOut.bitangent = mul(vIn.bitangent, (float3x3)boneMat).xyz; -#endif - storeVertexAttributes(vertexId, vOut); } diff --git a/Framework/Source/Data/Framework/Shaders/FullScreenPass.gs.slang b/Source/Falcor/Data/Framework/Shaders/FullScreenPass.gs.slang similarity index 97% rename from Framework/Source/Data/Framework/Shaders/FullScreenPass.gs.slang rename to Source/Falcor/Data/Framework/Shaders/FullScreenPass.gs.slang index fd46108ad..66f1421a9 100644 --- a/Framework/Source/Data/Framework/Shaders/FullScreenPass.gs.slang +++ b/Source/Falcor/Data/Framework/Shaders/FullScreenPass.gs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Framework/Shaders/FullScreenPass.vs.slang b/Source/Falcor/Data/Framework/Shaders/FullScreenPass.vs.slang similarity index 97% rename from Framework/Source/Data/Framework/Shaders/FullScreenPass.vs.slang rename to Source/Falcor/Data/Framework/Shaders/FullScreenPass.vs.slang index 5fd5d920c..8fb6667fa 100644 --- a/Framework/Source/Data/Framework/Shaders/FullScreenPass.vs.slang +++ b/Source/Falcor/Data/Framework/Shaders/FullScreenPass.vs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Framework/Shaders/Gui.slang b/Source/Falcor/Data/Framework/Shaders/Gui.slang similarity index 97% rename from Framework/Source/Data/Framework/Shaders/Gui.slang rename to Source/Falcor/Data/Framework/Shaders/Gui.slang index 4d5377e3d..be9564347 100644 --- a/Framework/Source/Data/Framework/Shaders/Gui.slang +++ b/Source/Falcor/Data/Framework/Shaders/Gui.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/Framework/Shaders/LightProbeIntegration.ps.slang b/Source/Falcor/Data/Framework/Shaders/LightProbeIntegration.ps.slang similarity index 81% rename from Framework/Source/Data/Framework/Shaders/LightProbeIntegration.ps.slang rename to Source/Falcor/Data/Framework/Shaders/LightProbeIntegration.ps.slang index 0bfe612d6..094c6ee6a 100644 --- a/Framework/Source/Data/Framework/Shaders/LightProbeIntegration.ps.slang +++ b/Source/Falcor/Data/Framework/Shaders/LightProbeIntegration.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -38,6 +38,52 @@ cbuffer DataCB : register(b0) uint32_t gSampleCount; } +void generateBasis(float3 N, out float3 up, out float3 right, out float3 forward) +{ + up = abs(N.z) < 0.999999f ? float3(0, 0, 1) : float3(1, 0, 0); + right = normalize(cross(up, N)); + forward = cross(N, right); +} + +float3 importanceSampleCosDir(float2 u, float3 N) +{ + float3 up, right, forward; + generateBasis(N, up, right, forward); + + float u1 = u.x; + float u2 = u.y; + + float r = sqrt(u1); + float phi = u2 * M_PI2; + + float3 L = float3(r * cos(phi), + r * sin(phi), + sqrt(max(0.0f, 1.0f - u1))); + + return normalize(right * L.y + forward * L.x + N * L.z); +} + +float3 importanceSampleGGX(float2 u, float3 N, float roughness) +{ + float a = roughness * roughness; + + float phi = M_PI2 * u.x; + float cosTheta = sqrt((1 - u.y) / (1 + (a * a - 1) * u.y)); + float sinTheta = sqrt(1 - cosTheta * cosTheta); + + // Tangent space H + float3 tH; + tH.x = sinTheta * cos(phi); + tH.y = sinTheta * sin(phi); + tH.z = cosTheta; + + float3 up, right, forward; + generateBasis(N, up, right, forward); + + // World space H + return normalize(right * tH.x + forward * tH.y + N * tH.z); +} + float smithGGX(float NdotL, float NdotV, float roughness) { float k = ((roughness + 1) * (roughness + 1)) / 8; @@ -54,8 +100,7 @@ float4 integrateDiffuseLD(float3 N) for (uint i = 0; i < gSampleCount; i++) { float2 u = getHammersley(i, gSampleCount); - // Less artifacts when using basic perpendicular calculation - float3 L = getCosHemisphereSample(u, N, getPerpendicularSimple(N)); + float3 L = importanceSampleCosDir(u, N); float NdotL = dot(N, L); if (NdotL > 0) { @@ -87,7 +132,7 @@ float4 integrateSpecularLD(float3 V, float3 N, float roughness) for (uint i = 0; i < gSampleCount; i++) { float2 u = getHammersley(i, gSampleCount); - float3 H = getGGXMicrofacet(u, N, roughness); + float3 H = importanceSampleGGX(u, N, roughness); float3 L = reflect(-N, H); float NdotL = dot(N, L); @@ -96,7 +141,7 @@ float4 integrateSpecularLD(float3 V, float3 N, float roughness) float NdotH = saturate(dot(N, H)); float LdotH = saturate(dot(L, H)); - // Our GGX function does not include division by PI + // D term GGX float pdf = (evalGGX(roughness, NdotH) * M_INV_PI) * NdotH / (4 * LdotH); float omegaS = 1 / (gSampleCount * pdf); @@ -128,7 +173,7 @@ float4 integrateDFG(float3 N, float3 V, float roughness) float2 u = getHammersley(i, gSampleCount); // Specular GGX DFG integration (stored in RG) - float3 H = getGGXMicrofacet(u, N, roughness); + float3 H = importanceSampleGGX(u, N, roughness); float3 L = reflect(-N, H); float NdotH = saturate(dot(N, H)); float LdotH = saturate(dot(L, H)); @@ -146,7 +191,7 @@ float4 integrateDFG(float3 N, float3 V, float roughness) // Disney Diffuse integration (stored in B) u = frac(u + 0.5); - L = getCosHemisphereSample(u, N, getPerpendicularStark(N)); + L = importanceSampleCosDir(u, N); NdotL = saturate(dot(N, L)); if(NdotL > 0) { diff --git a/Framework/Source/Data/Framework/Shaders/MaterialBlock.slang b/Source/Falcor/Data/Framework/Shaders/MaterialBlock.slang similarity index 96% rename from Framework/Source/Data/Framework/Shaders/MaterialBlock.slang rename to Source/Falcor/Data/Framework/Shaders/MaterialBlock.slang index 71977ec14..9e7882a4d 100644 --- a/Framework/Source/Data/Framework/Shaders/MaterialBlock.slang +++ b/Source/Falcor/Data/Framework/Shaders/MaterialBlock.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,4 +31,4 @@ ParameterBlock materialBlock; float4 main() : SV_TARGET { return 0; -} \ No newline at end of file +} diff --git a/Framework/Source/Data/Framework/Shaders/ParallelReduction.ps.slang b/Source/Falcor/Data/Framework/Shaders/ParallelReduction.ps.slang similarity index 98% rename from Framework/Source/Data/Framework/Shaders/ParallelReduction.ps.slang rename to Source/Falcor/Data/Framework/Shaders/ParallelReduction.ps.slang index a48ee680c..a5a33afce 100644 --- a/Framework/Source/Data/Framework/Shaders/ParallelReduction.ps.slang +++ b/Source/Falcor/Data/Framework/Shaders/ParallelReduction.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Samples/Core/MultiPassPostProcess/Data/Blit.ps.hlsl b/Source/Falcor/Data/Framework/Shaders/SceneBlock.slang similarity index 87% rename from Samples/Core/MultiPassPostProcess/Data/Blit.ps.hlsl rename to Source/Falcor/Data/Framework/Shaders/SceneBlock.slang index 506c067c5..0e39eecb2 100644 --- a/Samples/Core/MultiPassPostProcess/Data/Blit.ps.hlsl +++ b/Source/Falcor/Data/Framework/Shaders/SceneBlock.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +__import Scene; -cbuffer PerImageCB : register(b0) +float4 main() : SV_TARGET { - Texture2D gTexture; - SamplerState gSampler; -}; - -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 -{ - return gTexture.Sample(gSampler, texC); + return 0; } diff --git a/Framework/Source/Data/Framework/Shaders/SceneEditor.slang b/Source/Falcor/Data/Framework/Shaders/SceneEditor.slang similarity index 95% rename from Framework/Source/Data/Framework/Shaders/SceneEditor.slang rename to Source/Falcor/Data/Framework/Shaders/SceneEditor.slang index 979bbb126..66676735e 100644 --- a/Framework/Source/Data/Framework/Shaders/SceneEditor.slang +++ b/Source/Falcor/Data/Framework/Shaders/SceneEditor.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,11 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ __import ShaderCommon; -__import DefaultVS; +__import Raster; struct EditorVSOut { - VertexOut vOut; + VSOut vOut; #ifdef PICKING uint drawID : DRAW_ID; @@ -63,7 +63,7 @@ DebugDrawVSOut debugDrawVs(DebugDrawVSIn vIn) return vOut; } -EditorVSOut editorVs(VertexIn vIn) +EditorVSOut editorVs(VSIn vIn) { EditorVSOut vOut; vOut.vOut = defaultVS(vIn); diff --git a/Framework/Source/Data/Framework/Shaders/TextRenderer.slang b/Source/Falcor/Data/Framework/Shaders/TextRenderer.slang similarity index 97% rename from Framework/Source/Data/Framework/Shaders/TextRenderer.slang rename to Source/Falcor/Data/Framework/Shaders/TextRenderer.slang index 6b61ea5a4..29aee0309 100644 --- a/Framework/Source/Data/Framework/Shaders/TextRenderer.slang +++ b/Source/Falcor/Data/Framework/Shaders/TextRenderer.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Data/Framework/Textures/NextFrame.jpg b/Source/Falcor/Data/Framework/Textures/NextFrame.jpg new file mode 100644 index 0000000000000000000000000000000000000000..846671eec0c0ee094b4ce0384782cc2f40ea69be GIT binary patch literal 19067 zcmeHuc|6qJ_xO8e?E5wnh3rfAeGAE!M0P3@GlQ`WW~_;)y`~j~s3gjsw4jJmT15!$ ziV&$Vq+!hb?t4UfKF{a*e7@h;@Aud5xnA>rpL_1P_nv$1x#ym9-xu=*^8-hTZ2}<( zf}EWpH3)*ZAU*^Hp#X${9)bsA35;aHaM@V|$4@*G(1`qmK}7`mCk&_%k`Ozq9SAB! zNG-zU2$dyubs*3Jv=aQTA#_==7=YXN6Ck#qbw7cYWC36BpT_;$5Tq=$@V3Sg1M#x1 zAw)8f6he%WwXu;^@g_v#i18#P%`X45JqPdx zy+W)v^9Az@#K#&B#>N>!Fuy_qtQ>^<1EyoXhW0>wTwGk-TzuTze8RjuyuxTfK0ZOT zgs3Q5R8&HkkClGD<}-hOBKUcE`33le1O$Y{1Ox=c;D>ozVLO}>58#@Ol7dHT-ys&s zFIM009{g@(?;RY!KdY$bLC?^%FoZ+`ZrNBoadNO5uvl27BfSA@D=Yu5=rBG?t`4uE+s1z^P==)B%_%ScbFpG$iBu7FqcB94P-{f%yvJL$Uf4 zhAg3PDOGdfpBlYNF1;%u1k|T_Zit+&BX+FXP2D{E)pnQ(`OMh6erl{yts}bdZ|{xR z9og=yQS+QIV-}}nz=WjP<{B>uygPX_m2i+zVRD!5GF4#tAp$8FsMEB5yMG!(hfy+> z+*^gk=X{Mjne@fLU}!pQ`&=CpI?jZWhI<}Qbw$+OGBlZ$^q)JI9WvuWP6_i4o^q!X zVjB+DNSW7_yyrqx8_#qd`yutVj+FYof(cd3sH#qTWO`f8w6&*HVRsZ<_{bagpv>u3 zb+=1c=$da|n2_OhH+SZ0&E|sO4=FOEQ_NKK02313vF#CK$L(a^s?=Lf^aPoS z{p)4M42S0~u9^%+FqD#R3{7SCw6<8KpKcpp`?hzBk3hlbw8Q8+{q0%NQ&;KkIIpTZ~9BhQQzb92E2MdF`=GJZG69c#^p!hi67tNH_kLqxs+V6?o-@mY zTB?##e>5DX3>yx0ecslxox4D-F{G+R>dlkTnblXZ{&QW7V+{VpGc-%zlpC@8 zuHS!`e}*7enp@!0%k%n6k#r_w`>n%FX#Fkhh|G`c1tXtdKc6{t-^KHl4azd7&^RK# zXwBz{my@tmnNX93Dzm+UVQx7foz+G)OkR`WI^!zyk&CqG4f)DC}4<_5HwuIyE*dCmpQ4I z7$BzO>3M9!Z`~~~`r=nAz%nw{9P14Hw5ABi?`zf6nSO;<ZLIArjR`%~n^P{CQG7?188_&N(tVxL6?4oz%pl0JXxLkR&S+R;$5SS> zdrpR~T-^N0vuCSkb!pD>%}|)cGnC)6RTW|O?O|^2xl^elJq|NPTlQyP$4~!`I7lXK=+ra*VEL7jMq9kv%%X0Fz745HUjS?ESw?>C-iB2 z3svimME?2h!4=K8uA8hfQ&!mkcnX0ZY=9w%H!6k@Np^`PN5zss7T&cWR}$GhkVp(? zQ8-4D@%YHt2)F=-;kQkA44fBa)paK%z!YldkUNBj$WSa41vdrpL@iQ-9IKhN;pE7l9PtIl z62i%ZNLG`eoSQ{$LgUNA=8QX~Pb&DAw$6rma#fAm{6vVugAUFH9 zHh>!x%#s^URu)H$4YVS@hqJ6?9~1d6xV7Q`O1Cx!=iwPiwv%@Yhv#-NtqsS?{uSPh z6i#O0T@%8sVs0pwqHzzzL>QO1%5 z7u@#3>O@+B)e3$;!9u|=vjIiMhQrnkwzM39#Mnq2=})T`OV$BpETaso`l3Do5ZbGLGw(^u-w*LyV334aGr>Ap{c^ zZKka|+`0=32io8dh$R#4@saqL05Tp25RgY=)Pl+9nJ0!ZaEW6?uq=4}zaBp-HYWVH zje{lp-!aal;NLeFj(~8oM?mmzR03Fhc(?~Xf$T_f@NnL+V7|B((7zFLhY(|utiuVx z3z8C?AH2f?8jPG1hYt#fWtmg%ID8EGFUY+X(7%)O1_s*@!-+9VgeyGX{#yIR0#F6o zNQ{JK%}pjofo++DUuv&-!hvD?7c5^O%Mktxo)=7U$e+nst4;;(2<*E+oh`>$@carB zW5KWrv#bppOaZZj_=dGOw80zx9fu$(7%vY=fbi^}@a>=Q?Vs@NpYZLU@a^A)Z}YZw z6)>3)@KJy*g*^>H$Qr_dkS7qhEV3W~3IQ+~{75V)3Q&mr2MSrp2K>RMnPt=GeIibl zZv=W^eh?T7A(Nv_wX`BhngQ@}p(d6Xp_LF2rKO{(tp%A|CPW2b!|-I;Ks@jcEabk` zHOk2na29gj2F}{fQ8xGxf7MG(itk}c!_r}IOIh+1s+FJ-kr zh{G<@MFAIu)j1BUg%8I^;3LT-AXgoD_zR6XJ1>&|WwQ|xi_|2tZ5-g}?JG08yO>i0Bq3>ems&($nN6cFc6hz$tR zfj|N(k@$1nx#OdMLg55imV%lFVBzx;3pp&TJb0YkPc-2#Cg8sl;C!?3Aj6_~7FR1wcobjI`#fAh#2IGM-){^_*59PmG1phh*f4N%!Zw*BYw!rhc zrS)gEW0ftj)?gA@D6j><;Nsr1W?_%`=lzca{*k~x68J{~|485;3H*N~fnU)pJ`#9I z@gO#3_VM`uM;GJj=3?i#!Il+f^EqrFL=urg5EKEvxxt>VkFTFBXDc|y=LV57IIs`E zlA@g5?ck4;5b%m+VfPq@eps0{$#7e~Oy1Nb?B6~bd92mpq^^N2;@1HeZG*2%5{8IKKtZvz25UyO$v zI80zO2lt@(f|tO7OJFiMNe8r$4KXT_b+#<4f>o6T;kU7@13o?+PbO=)f+K~17#s-r zBccK#6Cr4R%y2D8Up0T>i08q#Ch$!S zb^I4D@Dv2q?1dnSSHExy=fH{09tf&#TQVMTSYMVLM-cE>O<16d=|2)IQvQ2jNq?Gf ze~V$8?7I0+5wfsWW5K6|*ce$7IMKn&YW!J=|JN0lu(gDb)o%D8d<-5$+_GLk%fP2u zFx`T1sI&yYXFEAG9WRGCM47*2BE&RKy0GC5NgX=P=Z*T zw+-B0;Ajnkyk&;w^&Y^Wob~+efjkDXND_hdc?+^}^N_{H#>CB&u)!~O@Fi9d5`&gQ z(vUo)0%<_HkP&1CSwXhYddLOxfP5i;2n!DHBEVgScqkd#3GIUpfo~-zp&Td=Duha) zOVAal2C9edK=+|1&@-qDdJVmY2B6Q-81x;Q1sBLT5&Q^I#BziTLJ^^k&_x&{)*x&V z8xU>?U&J;HQPP5PPPwhV{8m|e)bjY>g=ZMj_f|{!R+zu``9ztFS1`}Z)WdeA7KB^ z!NDQUq0C{#;lSa`5z4Wh<1oh=j!KT(9PJ$a98;W}oXa`YIW0I{I0HDzoclOWah7v7 za&~YIaQ@)p<&xpj<+9`QudC7x!UH#}5cUS2s~6W&d{1l~V*vw1J`KIHwtOXCya zQ{%JY+rk&ica*P)?>65{J}N&yzY@O{zaKxD|0sV6|6Trf{67Rl1vCX51#kkX0=WX! z0v!Th1-S(k1+4}B1(O6b1+NOW362VJ3n>ZN2n7hG2;~ah5b6<{5*8KK5q1%d5I!ti zD*QUDyr_*RR&=*$p=h({fEb6EvY4Y-sMsN~3b9tP zF|-I;AMJ%sKE5lQNUSN>QY)NWEHtT%oqY zZN>H#1uI%sOs$k&X}>aR<*Ai-SB^@HOIt~YN}rH!kRFy1m02T0kU1gKC^I69mbI1* zm(7;FCp)%k#VUtYv8(b|wXT|#Q+ls+j-C_5@ADVHg~QQ=pyREbtOuku`#L)BO{O!bWF zGc}}|fm(=Ku3DQqQr%FUpngWZeKq@Plhu){&#&&$;M1_yh|?(3_@F7Sxj}Q6W{u`o zEd?zD<(r&{fmL>gMV`*W=f-(@WK>)f?AW*T?Ih z)qi0iYT#_J-=N804jcu=7+x|QHc~L!W^~%9$5_nR)%dXSLlbtBwI-=1^(Hh^LsPQp z71M8K8fIZ;rDmVZmCfb1ma8lSEYDl^S;<@BtS(v& zS*u!yT9;dYU8}t|X6^O0(>A6yJ8W*RL#?x4M_JcuD`M+un`8UVPR=gauFP)C-oSpl z{cQ(!2PcO#haSh3j#$SM$FcQB>vycb=fvma?v&%yw?S<~%!ZrJNayv=8P0DuDsGJ2 zSmOe@IJ#uGymeJ}jdrcu#IebBQ|_igHyyWRw+HT`?ilw{_wOESJ&t+2_EhnV^=$GI z^xEQ8;x+BP&O6=vy^og94xcB!lD-7rT0c%dFTaa^Q=4r!pWHmK#bC?6E!|s{wlGjJLq2^(7e#;Fqg2RFlM-Scx41v#I}eVks^^{k@tww z!~|k{lzP;@sQ1yP(I=zFVw_?wk`Sb=q*}5VaHd*fRbuzXeu!HWmmBvZ-YfoUf^b4) zLQA4r;=#nBB)g;w+u62bw>KrPO5U0LVTbjO{1ik=U`kV}eCnRmfj{j3DBj7lGi>M6 zUD~_ScTMj0-hFe=$~}MV>ECO=w``xlzLlIY z%tQ3U*u#&H=p4yD!aN#uwDp+bv9rfHjz=EvPP0raP8Uv3O7G8b&Zs^idxCOe@+9Wu zqfGtGyeyuq*sS;28?tLpDV#cfik=ga)0Mk6xAOFg(+5vapTV8!IBRwGQr^nELwPgj z2ndt5Xb|==DQW|&iAVJR=ziUf9u1#4=sJ}eXskm{hvRQKGFyF4Dt_V4#^CaeA4|? zKWsPrY-IDuz~`vXv!naI2!A>KRpsloZ&u%0#(c&G#$(2r6NjnGsYR3elTA~uQ}4b< ze4m}B{8;{@m}X3SFylQlG#gLnq-W2m&fR149l4lL079{W zC%8h*1}An7c2?rx;N;}uy@^9K0NhCz(@o3gM9*Id|DY!xIHnM@Yh2Mr z-#z{O6)dUAV4ZSC!BxX$9ys#djy^7>-P}A9%lDkHwJW^l)#<4sInhsruchN-_nw@; zn*MKYrW-r>gd`luF0Os};?oaR6GvY{;=xlTHy*tlrm2~(_X|y;%l-ID9{OK;Qv#`E8> z{QIrWqE2#U~#1~6KeHmY@(Gox@d2HecbBD8-a^wjW0}o&n@vC z>Gqo0yXSK>A!g@?ll<14MH^g?Hg9n@GA?_P{FZj-W2)klPhqC_gf^_{ScaA{_v|-H zrJicc$TaG8ztQvFDsIYzl2Y&a`K$|ljDDSZc`w;g@8uh3%W2n{Ll5bDqrEH2)S<^V zsAq>g>;H{RFw71xo~laY#C@+HH*D^@K3m<-ksC=h`Mzn3C;fhEYY<>Rbfhsm=>lW_ zxe>irn_soGS6&R^;y76m?M8o>N0n;HauE5v>FfuUM2@r?<>6-68T&!b?8&+l=^B0m z@>3{XPTBA0FG^1eYN~1E;h4FocL1k#a zMvSHF(FYT`Pj^1G*skiH8f#qkql$hvG_i53XJ}>jz-LBPvyXGisAsw7>~$uzCV_gi zOe@>Gd0=1g*~iAW+<2vRjs+t#>GORzIRn%RhDKiM*&^ zRWI5ND0D%&fz(soKC@htcidP5ZyOs*<66rh@zf!c`nNQr@^v}4MqeGx{J1*J;p4L% z#wQW8pK1I+u3a=Dt)bob)~H|XD@j&L&}`;4NQB6O;hbaE6UtUEjYMh5Y_y_DA~u;5bKQrpfn zKVgK>4hF^wJs>>B4Wgv)ei(`Np1k!Mog$pt)~#_dOh4PG3GrIWm64G5#i*sPESj}m zJ1?rWn3oJ4)25j+R@&G;SG<-f&Q~vSG;g)%tOgS@-p_!`G_I7=E=fqd8=06(*W*0mxe=w}- zv+9_~S~@PeF}Bs|jbqB_R7vTCGn&br^NgXLgvU>3jYVuPqTAdduic9`~$Wut%lM z`j?Fp3PWCy)d-Y^!}Ma%{R!&Oz{$5)2{Yw=@08u|p87J`w`;B&6a+G%yn_U3eD&py z|Oa1#1km9C{`^{AB1> zjIWe8klDUc8}^ z7F*hJj}rNdYIE##z{u$-YR|xl>NwLQqcvXI$5mP!&e8g^zSYqCfpLw`NHrN-1n8tLFaQk*(^TayOpOb9)5K~@@tPIZX*zPV{X zI&}>bqE4wd^9~;!jZ#VBlrG}APAMQibDqmaUO#0xW2c6Bu1xQbr}y_}Fd=1bF6SUC%3H6m<=E$bB%%7~Ecdl8drXK?B5_CPdEc(73}G2>BB zr_X0U-1qdR=FK_6okNt%Z{`GVeLj=@f{pxNVKU!2doLAy_;-pO1kUl{(3SeiVQqp7vup*vzQXV8GS&a}HQ!JEgw ziEA5|0cZa0iavO|oZk=|fa0C56Onn?YnqfA^5V%2Cgjmq**DZ;Ao=bhcgf7XlT2ur zaj7%B*jrND@`F!W>Kize?oh5ai@T)8$X2W@oMgCD8grQtZRTu&ZkUc=t=bin{4WQb z#%09J(DsCrbE|?kHe3OF*IY8#Ur<+TJ~DPmpU)g#&xFb-``^aLdDT3tUlye>Zd@Qw zIqW&kImCqM9|Q-62Inq3e?}M^o;h85Z^Hq0$2;p8+nLai0{F3|>NYA(nbnnTRf@2; z;d{#eT*W6^TED4s=cV$m;TID$^F#25k3W3Bi;LD*yag7i=lCOqjaTdjRyup`1fJLq zZkj!?m8)obX`YvcUuEW2Y;lYTQ`66UDlgdp5ItjtS6aaJ}+EfSh%gJZRfm<=p9|VIX-5VyB^KksD|mF z?#QGCRt{+~Quz?U1Z@1j%K8DK1}W(*v|Y2@lKBl20KhP-_M z`qA1=w=HbQ&Hh5xQo`#4D_CCG#TmPrl8|`S*rKhD5o$s<^1_`1l!mW#@GoqqC+My8 zYtP9ql)En0b;`y12BbY=5Bo;JbS#5c(#QMzX*~mFQj@2|Cw%UtA>Q2@Q~zvb16dba z2#8s-e3yn`&`M*baq5tCqi$j5Kn0q7a9C}sa^?O`u&JUYGiI`;iT+zI?4ZXRm-2$0 zd=41hyftt1F{g4`oKTOi`f10%rX;NJoKvlNX}Q{y{+tQ5y&;zbQZF4#QcyDD;}Lja zvg*h6y_(JLm6~?=9{t)o`C-|&El2mMF`83{xUUUfp`U-2*j*8^yo3pPqj%)~G{gOv zT+6h)EWnB&&%B{i&kL|BQ z6R65?kH%H?4?ec(Bzk?_Y!SR6r`0FBjI`D?q8A-l3meahUDj-|D|NhwUFh$ABaJ!x? zlG{<`PaL*rxSL$%O|=F)a-`9NHdBZ6Z4t!@_WnOSe>~c`m2$;WrG%(6jIed-#-3wB zI34frHedsfPCk*R_HsP3wy-Mt4ciFkdP_iO@yK&y>rK#F)y{~edb}y z27>lbZZn|*U~PmY%0^hex#uxCy9;M_6~D=#)EiN4sGW~xvP>hox-XR6m1^2sn}s@e zFaL)ySe09W#6`QzU(94>Cg++x;MNeXJ5sD#`zqTFlP1~SSQa*&feEd-^7hTf<})7y z<1Nj3GFSpGSiM@yK)pD8wBp_1;D=qde4)PC8CH-)*^D4q52VyqE|-9d!@aqfY%gMP z(nMCrwYpkg1ztLR3(s~*Z0Ovp>gs(RTwmT>rLD`~WWPtws>+v6ryyF4aj7G$O~9*ZyD(X-K;8THs+ zrfW2CiE<#6{YpwcrQtfDJ4c-2-D=8Nd-G)A5rkqk@FwY>0efH{I^90;VBpr?FK3SC zOTCl3%yU@X|EzHt?InE%l%1y@8*C_vD{{-xusBm9dE<@fUW`&*{o&S8-|XjAZ{EZ1 zo7ZiHXOYp~R~hapc8OH#_RZJZ>6Har{4MV8ok9x=3C0j8jRlpoyI{-v{%v4rVC&Je zA+46Jim^<{tl2UD>Wx)jD&9m&?N#yF{)N)$*?&aN_Ncz_c^);Db5H_;QVBK)mbX-_&Ptatq7^SZopM|Z}s2>Ss@UZ^ooafKm8PR5pm_Ox4Ne)8G3P2jFr=NJ_> zS}EFBGz$iLmELU1I^loQsQtCr)<)s_DO$#$rNxN%vRBRY8h-W60v-8eS8BZ*2**ox z?>BwkRC~0$Lp+Dzqb5g5ciG@MlLz{~3Wo8D`fczkAq7wM;6G0b4f5q};R^PzQyp){ zv}=8Ar1w`2^n5&fD6A+k*tW*$VS#6!t4c$`WvQHw+Ol39*m_{*9X)R#ySqD!fTI`X zIpn13BH7W%-Hf>}Uawj{vW+4ft!O_hPq_fZ;QeJkeTH{qdVkwYMVEKyK9Tij8|~B> z>90rgGSwJ*tC$yT3F#HcKUW)%T?7k6ltp z1#x^@DmAwuk^cPow=dxasukq@aX$Taa%Jum4BI=*R*bX4w4VMIhtQV$b*DYK!QzSE z;`!M!6O$pGMDObQaxG6iB+IB{i?Qpl6dFAZeCIJRdhn0xdH5GJmkz4^;K2De7y%LA z;W|m_>)~#5=h`QCSg!4?Am7q_sS#e@@AG~yJLD-Qk*^094t#3DgjlB`4Njn)r!$=? zj)@f-3OWzF(}|3GeU<%Y{=T9<&okY;kNQ}+ z`eW&?ILKS1QcK}X>kunG_mWVmjXf&y${TCziB{;VtZf_;2L?qTd79QW_*6%eELCiN z>#6j_c)!}&Gx0wTTvTHeSjwjkNm**9D(FjDm_5l^jikwMica}-(JquY?vCCe+8J

A$)&JRfFmbs-;{j&iRm22}6qXZ*l8)IA73mz0nkrSxdd^qudK9d-c+MG^( z<(CY?N{7^|lYx~3LyVN}%rCS%!3yZrs|p*?%{CM2U<9wO%5%8eZM`E=T3#dCYMtYC z;^^LkmX+vDEC%o?9ut`C`hns*7e7hjX7;vx6~^x6mCV@qqHH>s5d}Pn{K(@zw0>&J z!!o1g<&(b4NWJ}>=#+k1*yA-@CywTEoErF|^m@a!8nBJCh34|t@p-1RHim^Gvw3d{ zUPSjAOjAFm<`zxfa$WtA^HpiI_xQoV4)~?or%UUYk^2hg?xIsQ^Qe=X_cYKS<+$6WE2(3O&?%0KJ-#XlL{c)I z@p(=WLA*ro3m(J_-LDBF=6||+DIK>dzQh!L#N*6Uhh|I%DD9!YZyWgbk>d>dOLL)L z&nYEay|=BDCPP$;fwXbiH?eg`Jmp;voTr>~MNs~9?pHbQ_K?B2N}n~}lS!9Otm41Z zdPYxv`(AKTQ$k&NB2#j(grN0$-=M9*cMu6?mtl*WF|`=#2(MzQ;i<#hjvHcM^JGmD zbSL)NGH~)^w<`w@F;c<;4Dtr?!||n$xmq-zM$jWxST8hq6&QUI*K{bo1T(D@9nA3w^aK??R6%}6&biUCrTHBO{jetz0Z<9 z>ja#eI78_m`m-y}o1f|!XG)oNs|?qEG|TC`F!K@{5Pb2~-C%5FW_R}|5fvsOUz$DV-q~wO2_sSCN|X);DYX{~&|X z(z}cuv$PV8=8rXgEAM&5je2*fINM*os1khE0SqmU5ipW8by~Hsb@c`c?kr9g68Ldp zjytv0l;JQ8;`KF7m)i>tvd`Qp z*2yPVAjR7+xu=`82KH067|}LVqlag4&-FJqYQ3S>oJ!-le@~m^Bu8%FKD{EaS?Dmr zMtCG;R{J0C4W4#x{6J}V0dzJ7aD`0?N(l@T?`epuPSv2r8Tu;b;8&dqYq$Qk!E=r) z#THc7X85GogpB#n+-(o^ma5ct;axlG5H~6en=y8aYwkC-VOf0G=cq&bJ-)%Tk5Z)CpwKPilkHUIzs literal 0 HcmV?d00001 diff --git a/Source/Falcor/Data/Framework/Textures/Pause.jpg b/Source/Falcor/Data/Framework/Textures/Pause.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ed4059b1ead42be320d50cf8c6edca158c37dae0 GIT binary patch literal 15778 zcmeHtcUV)))9^{BBUMm=&_qFc2QfgX3W8KY6w`o6NFWKIh(s)?pkhH(^ooc|QxT;o z7Ho(e0YU75R}l*$MWyE5lThLqIuR_cj=RpD7n2efmy(l| zkd#xBm6DQGQc+M)QczHllM&CO-5##qKu4;BAm%63aiNdCk^ng zL2_b{31osoXhBFh1WFFUe+34K;@?6LfRq@7AOV5lgn|%AGzN&fr^Cn{>2 z-wgysB&21Zti0Xw{=2n%aAe|^{RLHZPdk6eK}aN!7A=qohs9V5M9?&qM*|7sv=lIA zsk;?n39kD!^qA|A+u0lT5dD=X9E)$dSXcnuY|9&sK#2V5RM$W&F0Eryguj4r!#N$nLHDO8KqnwgE(FfijzZ3b;qyE++&_k|+`mV+GE$Yi!b01q43@`XS zYbZrML3tfY8#~lnI2%)b+2Nc$lf<^`BYe}O)O&r5-}^ac;9~y62bz3nKx)N-J-PhH z1A*@dZy%_BD5(0R#rv+BbgsAb+k4!g;_aGK+xSo`HS5rbyT+61vpto2e^k~6kF3#X z;>NYT%)Gzz6mM(OP}Nd(*`uQ!1K}fUcpe{y#v@*6omSe)G0AdtJft5RLq29H-1j zIop01dv~qVIMJU>$!Ib?nBkewX;yZB=b_hcyVsVFyfZq{n8XS^N*S29s<$Y5^A6hS zm#9NGH!NIj{7tJ)X4{i__%8{*Q4S|6&R!Zjo#ZStTUlKir6H02t&ox3>`$B_KIBe8 zE$epk^x;3_f062*@?hphCXkJ@r#D(xu%M<1a*<3xvlAfRfgGm89tDCVW7%{j*C(DE z8_xwr*yTdL9Iii^#bO9JJegb?jTs*U6JQvA5*ci`EGww%PhSmlIMcas9hfX^lg3_5 zilGI#E)4)EVQ0b$zHAmN!k@;CkM$w1q5z^4~nPiJuHOhJ=?E-v6EdWS3$YQr9G0%-`csS(SIjMyiZOJ^}TfIc1O zB*t=?6N3PQB(oWnZcQ)ERN(ROcq>Y7%r0qpvS4Eohi|g zOrrtQao=XNqV zGpP8#!uxR;Tmjy9HN%m8ZGvASfkxr7*e)b42_At!NZ&|b&V*IKFw6^{u-K3nEkfAL zAG!?yZ`!}=)}O)<;(gf^#}K#$L7GNkvtomzXkc=Ih0>Xk6Y7(J34TB(XBL;siea#r zk>gdQCMd87B6#TuydOO>N|YcuK>)H%^22Kd^+hNHVjg&cSJK2&u#RR6XforSf(Kze zB(yha0S?}KgbFxSC(G)BR{I2HaX}6;grFAKm%vG4e31aOfOkNE^?-~cA^?Q}a?9`!J#s=*vckc8OrXKYaa39a zDPG_@#S>_3?q9G6O`v~emn26LSqv6iM7eUp_MP1)37`tJn8k#3EzV`df>$tyChD6c z7{H1B2`fbwIK4mNCBYO&{gGX;?&iQ9fp;u0XF`tv7OpWx0Sue4z~7)@4hXFSm%-m; z{J#r=X2N(eNCm_e|HK~u#2){|9{=ZK4}o9N1a1KWyLI3%V22?KIYCsgg(riTG9J93 zqX5hWe~ti(1swAKh64`~!4Gyng7$^`7Mx%Q0D9oJOLsu_Q_~jf*GKz#Gs(>+7W&Ej*p-pcQQCZQ>nEq(#v^lGrr=q{RW0 zq-csQRg16yKYz8|>X_IV8kdA$9m8O9>{dHy2^zNpFpM_V!V6fq(GFUIa~AwkZ(lr- z#irpcjjRkQrWR&+Yg;2zD-&BYi+Ol(dSq>EVr^_{ZV1khOswq8ZSmtt3-rdOM%eke zxQ+J(N)B4%gGx+HG)go#VzDEQO>J#$jZMsq&CCn|haqPTlS^7{$mD2GwBSPHP}p=q z?1G0|1pD%Mu7ehkR5*l~*vV%9QdbjMX$)EnjmhNzy_&+qpJ>$E zdy@Sxn~jN?w6 zPm3Fe7<9bA#Oz2EID&W3qQKKn149s^>3^ZZ|4aqK`iUU!{FjCyWaF?RxQQe-%_$P7 z``?(I@!yHgAtn54!T+oOTq3i z+}F>?&C|0U|hyB~duB-u`azw`l>vcWeLgfHMXr-JYcz?%TfNaV)C_#OZ& zlA~c52_LB`vVnjAR)OJ2Aq;;?P$UasM;NBYFsXnCmN}LhLxtg60N$Dq51-AVya1e* zK&K@F_z8e@81XT50K3)+UamV8o{w4ehy_W9z>lscn?}4gU00=`hrNB#HNBcHztlpZu06v(Yc1_{P2O+3xGX$wTpTg-J z1qT(IAgHolWIW2Sy@*8NbQ;A7R_J806Zn0ogidE!w(Gj%q9ydLaLBDqz%o13?Vbf8nTBRAy;T2rz$fWMC>ctH(x41z7qkz`fpVeaP!V(nItNujm!a#>ZRkF@T+|G`fZCvU z&?l$|`VI|)i!C^WG(rKPiqJslA`B2_2phx#ge$@e;fDx8tVBd2n22~pGGZel1F;8@ zgUCY^A<7UJ5!VrS5ek~VN0>sunpL^ z*Z~|4r;0PcIpBP7Bper)hC7Hmg{#3e;@;tYib;xTh?$AGiG_+W#Wsj#ixrDq6>Ajh z6dM+wCax=PC+;Io6<;HsDSlkMTD)HTo%pbXoP?f)gG7MDDv4By0}^K>Y9(Gu^hrue zYDwBkE|H{5u9rL@c~1U=lPXD1YQ^iq*rjo8ws?w}7q>5K{RgG5Nty-n}Y6g0S{){Cv z5@#HlQ9I-FOvRb@GbuCEXP%q+d=_$+{w%*)YiH%pdN6B1OdNYl z>Z{bV)UT>{X((tc(4cE%Y1C+Z#4F*Q@C^I`{4IQs<}6JQ&3Mf`%}1KUT6$W6S{t-V zwO(mUXxnHt}CuDcKs*J+j0DW`5{6KA$_h4T-h9dRA;nhVOs z-6h@Sk*mCGplgonYd0;oNVgKV9(POkweHtEFdmCMc6&VaRP&^G7J2q8v|hMw;jKkd zi~JYmEb8#m_hNfhdn3IUdhhjqxmb5Gb8(dqn^!1%x$L9#(%K}A77f?b051h*|UUb=4S{g4?U^pFdo zxX_@`lc594T$k-z_AbmaEG?{Mx!&^C%kQq3v4XMUO1Nw|Dg69O%*x=E#VbchKBQx$ z0kS9gFu8|9r0l1BqB>Hus9iJ%+8$bGghRxhh!2qtk$WS%q6kshQJ?8VdJg^DDvwpU ztA0fLL>EN!8NrOw7_pd@F&CNg%xLBa!S_H^;XpEJ!$%@G~(e@%(DJ)y&lo*66Rqt}zv=B}_su0~GHL9z zr(5i|l+bP@c?J(VOUVR+&e3VRtci zwd^MBF5DxxCuz^yz218(v+!BzS^fLM_ub95$j;p_u|Ix)+X1fw7Y^zi+;wmyCn~4; zkn^F^!?O-=JN)A}>Tiul9FLsIRm;uD9Xd)s+M4H{S9xsqvFvvqFW!PCKS;d(p$ddSyu2W&B9-nqOec_DZnY>cv(u}jv+4!>` z%T|`PoLhA6#(A6bXUcWU4_7Euq*o#;*HrdYMOC$32)WR3apA=q)%MjDmy9kIT-LmN z=!(*n%&QVtH(lk|B-IRDi@o;wdgS%DH^OhcxViM^lUqw}J*r()d*`;>?VEK@bv1V! z?p(fWclY8wn|oFFt?pMouzXPQ(DGr$BdbT1_15(l9@{>yZkXS2wUN+xy~(BN_7l%1 z_nQ|tH?#z{v_1`c+SW>K{qT(O?A!B%=ff}7zr?&ue>Lq@_Ul=%kG+}urnJqf?Mk~# z`-2Yuju&q!Z$JIc`F-TwruWkCvpY39i$0irxZLH|_4wnmkMBOkej5Iq_C@Z?;qE!z zI*A>$0@?&HIE`X8-2Ktc}OgsMT8!6kfQ~`r+qXTh9>s znr#P*F5Z3CHK=d5FmzQ?dQNfmz2_f?gjZ`(7@$rpS`3AX5qxTfZ}&Q3mH>&dajCn_ z^2&M?uQ#-7J1hB-+271{EQlMF_xS4`qnx{e<1|`!xNMvruoqk;R#Ba88Sr3C%YfvqKF!Q?Zw%l}QW zhX44#uhQ_}S7{Wj5~$j(S&_Ev(7_-vymOz|@A}1sMLI{;O9l45$>DX*<8^8zAE^pI zNier44SIa+KIT$+T~=Cp-tL7@>(-S8?{(rs1S}sq%)Q*qhg?F|swBA^Qt`ek2?TV^#MJx`XY@QJk<~~E>eA!Wzl8(~BJK=*5 zGIhtiZ{Vx0R9$^SOkRSOva;V%7JSc|(AR$&^X2sW^Gz$wq`Qw9K9-f}*it^`&xh)3 z`H&&cgmvUi;7$kRtEgigp6<`TmVHSNPhR8ya0vGs_dtB9(VM!$r9R(hJIj8o4Y8V< zlyH8Nh&o&zE3~KA$PhZ)YWj}szZ!Vx0WWL8;sM4PMa#+)h52!mXS?KDJ4!pN`Ow1| z?@#DFOe_w&cit-^>bV>Kq9ywKi%R|MGbKE=>lc?D#9SvR9^T>k!=|JkHx_%S@6Cgh zppm+U(-FNa(#B5r)!sAT1E=I;3R_FK67NymSd7nL z$e>fv>t|sV6<^+TzuhOT60F423qSZK=QX9T`|OYBNAk?G5+p8fD=hWzt6f2TexiBi zk25E>gnmsg=^E-Tpy*p(`COLlSf#9eRevmx4|P6$op_~B?_GIzqW}DoD9G=@;{Mw3 z5}PR-fU8RP7CD{ti4CEu#d zYS(>`wy`rcT&X>nTvWgRvFE9-dr7KI10AKG&BtN`7p5%yuw*^*_}qoEr1bsIf5N&c zy+Ifpb}LZKaXoRULe1xfK{|prA6Has2+!38jlQm#;lU60@7a}tv2SAdj%8x!PA>gY zev61~%S#~ci*C1!4_ACDOoMwde8{%9vS{$(nGDYTA7Af`+LW}<3T!Qof7iD7e8J{V zp9~j_1dgVHYxdZ3KGb`noO@t{i|4J(Z2e&#@GcD>;)2_(M@Dxx_%t6MfL0n@J3NO+ zs4E(5TeK92-CZy;WF_u?p!7t@h62>m;luRs1~*abe<~uG{3tPG`P`Q(Ir$%I!|S$; zbUt}Z=&aAt-gH{)0!{x4cDSQ-OB_(xnQg}T#Y?ZyZwJ2VstteoW@LEm_g5*NbTftU zRL3E#{_uH$+Z!3<)xYc?-Wt)wYFPO>O1q__j z|7i@m-QHBoI_Xo7aar6rx%5_<8zv}s6M940eMd~+|N_b8JGZ4CQ9$&=o7UTTM zIqN;eSpz}dPcGigTdOg2 zQ;8Qw==-9wGGi?6hpWlX`PYoZi%#2jlr|&_JV0MPn98-AjEat#;F}k}R%zVQ_8RnC z_Jmyere?6exx67P%!{aKkhX6WS~;Pr1bsgAi>Fin68NPcNGWJXs)n2Xm@{yd9WCE0 zb8~Y>tjdO>AM97xIdY+-j}HN56e&4)d}`WN`X;^RG;8p?SBuXe;`oV@gKC7U4khU| z9)kG@e^fd$+Op_&--7)b8vTJ8l&|ScJ>>&7C1rv4zwhr%ugN0}4(OcQyxYM}Pwj=0 z@oV8=1UrRa-Pz{5$k*&7H039`^!JTpDa3&g!hFj$M?>xH#btrTJvt2hMgOy=_ol%E z2f-ZNFyTY@Ha4L>GDaR06cr=~4lNU`tTEt3yQ5W~aPRr=%IvzP^iw%0_Xsffuv~fy zMTzIa>-*?=EZTEl07vrDEXBa#(#=y_mS0@hjGol!*u`ybFV?A)461{*lJV+ASPJLk zC#hsr-IaA+H%cG9l=vK&w60=sd*CPqyrN569;Y~W-NyRs@{)_2^VjZdE$JU$hBz>H z%(5+??AA3LR#lzbC6ljIodT&Bbf#CNpYbWLF-@;ImOI!#cBCf%j8Td6^n!+M!QDGW z%}UgVWwlu-pdRN3ch`pBtr{7r@!n9^m%C(-KFWNL<6uYWL*QCVD1`v}BssP&yx>H^ z$lEJB>Q2wv^XjF!9V$2!T43D1-k`rz0^Yif#RDVwQgvVBprN4;{=xdMO<)~u-cnQ> zS{rQipx})X&y5fDbuHRj&`=?ky5P8Hm;vkMd@%A>;ff6Z#fxR}p_lhxv=K^_cyns{ z`WBHi-VgkK`2KXx@*!4wjR~-slD8}Tw0@pdDH*B&N)0n^uF)k|yj^%UWraPt^of_y z)(-)FrS5!|{OXupSjpnY8y;`Gppy49I(>P1rCsI`{jqI?&IZ4iW0}oQmX+q+PD>f+ zx!pWmD(YHAeOK1aEHhapSSGzUeSPjb2)3-Y_g=-f3)cdf%;JK{#Vzmm-bkL6ayf7` zgjZk2($4M8QOPg3mM*R_FK}p^(6C6n&Mz5>)8CJ*xTK(XwrFEYYst4C0;e&m$g9si z#UwaISDiC8Ekj@u6l=q)5B#0SYQY;QJeK|z+zWOpgoRd52}i6OuU$Lv!~nDW>fRkC zy-lFe65Nl5r-Q(06!1D*dghFrGON>E=~y^qN*st*6igeN*V*%YZn*tm;*#wK;)X@W z8s&2v^OaK69>_{j6b#rS|7lzCCN_k9O%>)?^$ue~t@ zeV*p!>ZeNzy4Iy=CMIg7PdoiRV0e4rj|kC)C9)3$6PA|3O>rAs*0+{;rM#KZT!BKJ zI8|4mWpze>j6tvmD@82%>+iX?pRD#oRblTxp&NGZ7T=op%8^v|Ldlb03>a_()5`-) zW`4uFv`6}Jry}DzWb{YHLHMv1n0w@JgphMBm)mhCFqcZUzGOy4r|mHR)L<9J8ofo?26rJI}ueI&1hM?(#pa H^6Y;AaAX23 literal 0 HcmV?d00001 diff --git a/Source/Falcor/Data/Framework/Textures/Play.jpg b/Source/Falcor/Data/Framework/Textures/Play.jpg new file mode 100644 index 0000000000000000000000000000000000000000..82abfa0643651f6ccb169340fd79b9b13f229577 GIT binary patch literal 16342 zcmeHucU)6T)9^{9cLItckzSEQA6O0{lS;B?!ZZAt(YdX9|u%Xiv}}0gW*Po{%#5u@EMFSRTMx zn`sbwqV93vbHg^0&SoUBc^#fjGSt}?l5W^26S-3B2W{w*qP%%%$ptuLUZOgkci1P z5C+Jpw$VgEbC4%^1BnRUymsCPNSr?cqKhwt;C+N7`8kO48%)RRfHpwl!otEL!r~$# z;xb~QVlp@>ad9c!EIB!xoZKuKaekV32{M0tA|%AbBqSxIB_*ZhB_$>0;fJKWpo+|Y z(g5!|BqIzNK}INqDuk3lpkxrd7hsSm-fe^f;1Yx2C%`bAP!Ix%#$bhnMMTBGrzcI&^Wvt>(n;VRx;Z;~OvSz5S%; z%Zakumfn%j$fQlVC(G}&_I+rT9kDk3`7Vv`$u0&e?b#hm$10acEK1JyQqSQ*yhkG;!+xzb zW5J18WA=M^P~jP(w_$#I*_P3!mhNhX(_mj)PR^(wkq60rUvl|)%S8y&7K}udaN|XIt8XyUYmy2rJ z%k}96E;X-#;Ks9`VHq z`8j+pH$0K2@ua49a|`1{t4hm-+0hsJNAt+%UtPM^S*QS#eG|B=Q zE{#m@M3VN zR7PSPTmZxH+l9`8^HTh}L9`T@!kNZ_>%hf=HmR(|q&RA@TUana2|5#0@MkfZkwH{W zV!U5O3>g%PL;es8VuHU&2oD88REPs5Lh*1@kZAlAHOTRsai((^6C8<0B+}>{8iU^? zC>P;VyZA2g7D&SuZUU(f9@8V1oiSp+cn*!pV1sf+n9?Pl!872@u~8F(5J(9M^l)|o+`t|d3Z&ri z6lP+CBeM(6@{ohzi|n`r zZh@bq$t-3(A({#%7icJr5j813Nw^>o@Z`+oaF}s)CL?O1iuhy+%)$)3#3VkD78O0S zKy0!A@HRycuNBk>0S|};;0-+8P(lY!ut@<4F@ZU~kn^;L!mL1n(+UXZv1~exOl61Cec&a8_@y5V zzYL&d!B2>v@p7HfmrxXonHc{I3d>~CqG(gr(JcsW-H(q0ZAg$3IZO{KgUTXts1$&J zf>*^)8h23vF^qvryyBwp;Prn!epDih{)>y?3;$P)FFWd2H-aV6Il-i;U#KL>R60GF zn!@p7dj|XZOd2ZTN%SwoBGF7%suP_SH7O}6!Qee7(O~346lx?Xk#9IflBg`sUyz4R zqJJe9i->Yz(wVFo!j%!U@9Z&E0IEQXnG9IgA{=HsaKLQpOv@xn2PW)KSn&wH$@>#t z3`}wKAIbTvP8;qBcvzs$4ugDHu)^f|Fs#BDJ{?Q}p;v}=(r%~)x3Ff{=zVR;9Xqsm#iyD-=IGCIoOSYz{IxNK7 zrP!pz#m7-OBz#I7ox!$Au~+3cZUbN#ZK#UplW=0~Rr!ZP_%L68ybF^>#hV*g=#!1j zOz@W02F4af)+S~P@ZhM+($L7#(AZQT9C#U7*qB=5Cz2}YjYWyH33PRz=nLfRRVN0O zoSbZsY-+${MHw1fTU#3%nHZXw=mQFU_9_O4l%mgIt4+4xN@bH-G=4;chg$@@{X~wv zD&SNwgt+*rX8%%FlY|uV6kR-U@BGdwWJ4;Q8b@Vt*g&qv@bD)a_4S=1|I23M;-;wC z9JeIE(ceuN8}_<}fmAj#kwvDuC4nB*COeCtJV*TNCZ>w{GY?maw{c-nNg(zK264~i zL`?_XGfyLDkW)dVM739ik0-%w>6;n_n;6@enb??t$+NOCG7^yb0xh6`km}FG6K9#f z5Hl&X$W{NAltQ+NWU}H&Kt{AUQWVuNCY~C_@6OlP#*4w`kQijDm#e)h7@7f%MzJv= zTbokNEY0+dDW+t7OOhE?-^$d8qEEH3CQ-;1kycbotI7JVOmZSTfs^$q|I7MK+xPYU8NE#g!ut6A&_i}TwFt)a^&^IwKhV>i_9Eie` z#s-tRYGUOCQ4=O0It|ZPF&h#YJ{Pc8CBxHCrKnDzY5%nd1`b`3p0Kd{|pbWP1eDKP|XVONlm6x zIUId|5Pp(a6c8N7#giD|%v&&KxE3V)r%f&&EqMHG;S9RJ1L3(9ln)2+=M16HFpZn8 zIgMi;1!wx;+XZUZG%jL41XXN=pjpqRaq5RbbiVLd%Ik zsAUD91Ti&lJ|dyu7zTm}D*Xbz2QVn-KYw{3vp^Qfrtv=`KrVs7cyb~uNkD>zUl{Pk zOA3;QW<&EJHAoxMhfE+#$QE*h+@M8}9~2BNfmT3daL^M6z78itYoN8zCTJV93)&0i zLxs?Bs2Dm2U4SZ}D(D7u2dW33##^8cs0-?a-a&)VSLg@0x*~*-K*%9xBUBI?2t9-e z!V0kv;fC-*1R|CoRwAMh3`8Pg4PrfF8zLK#k2r!TMw~}nLEJ#xLo^|tB3>Z|5JQMj zBnl~xlt(HfHIar$E2IL}_QsuFb<)r{&wy+?7;ShO4(kJd-qqCL?e zXfiqhosQm)&PSg@m!of^o6)b(gXnRL1ZFNq4`YMz!h~U>Fv*xrm|Vjh0X}o z2t5|+75XkLCafZCBJ3_4F3b>KC!8yMTDV&Hv2efe4-sh*4G|j=KM{(^Dv>Oa<06$J zjUv4wKSX6jwM6YjgGFOR*NW~FJttZ#`doBaOiWBw%vvl!j3)M**gmn}#p=XfiE+hc z#dXD9#FvRDitiLJ62C6~RD4)MLPAr*Q6gM|Be7GWSmKt%ONsB2a*_s;UXm2a49NqM z<&uvjKT3&6X-GLqt&mEU%9Xk()hP8|T0~k?+C`cqoi2Sq`igX$^oWd{jIoTLOq|Si znbR`&WZuXM$!f~F$x>uD$R3luCEG2Bl2em&kt55kmpd+3E7vQJmDiH@l8=$!CVy7` zq5L3D7H5VF#iig5;I84i6i^D93SJ6yg)D^&3atvGipq*kid4m|isuv`D}I|bXO`nE z>a5IJrL$UQjn2l;cAFhLd-v>$*)NpPO1eq`O36wEO0`Pw=g7~ookO0JIp@NhXLFHr zb>{}oT|M{M+=jU$%JY;xl;f56E8kLnKTlzvzg)I`;+)FRb#)NZN`s;j8`s;^Nm zQGcc(qG6>Gt&yj3SL2(emL@@Svu1_n8?9MdURtSIC0ft5CA1y16SR+NKheSJSn0&- z9MXBDi_|sOjn+M&+o*@sv(TgI9nx!BfLUO@fU)4{f;N3|eJA}S{Sy6G1_}l~2I~we z3_cpF8-^S1HoR*%Ze(smH#%WlknrmZW z!?C$w^T}4uufg)0{xUfAU@-+|iShLI-0j)srR+uaD)t&&WVtA9(QR*W?;!7d@7F%MJ}jR~U!?CM-#xz17i%nL zEUxf_{Ji}3_`UGg@=x%;8h{P(4>%Ce7ib)~Ch%^MTo5tnbkNse=isd1ju7pT#E_ez zQlZO2i$lK=TnX8Pt}w%}w6OXmN=s-}*|#uUbUi}i~wisjJ>^wK!txRr5N7_y95#%<<2 zW(u<@UN3%Ad{=@^!rp{ImN)Ap8^K=AzRZyao2iCG?Zl0VuaXuf9Z33~9GZMFMJ9!j z(y&T*)z(%0sqU#KR-;#wSKnMSf6dx8uhN{-j-(^fBhqhXsAX)(=>5&(w^M6H*T$}W zu+C^*_PQ_Y3F|92C~x>}L-$6HjU}5TH?cOgZMNN9xCOn1w&meg)2;irj%QLb?{72O zmb-0iJ9+#49mYHM?cnW<-1#ueBCB8*b{At;>u!hLr?O?TQ?tAG`0gps!RKV=eA!Ff zdoR~4w=hpMFEOucpU=L_`_=dF+CP>bo!@f6`9SHxxd*o%{C0?P=y8E#!MQ@^!fl14 zhiQk~k9Zs@KdN~&_ZaqA(y_kdp~vr@usTtEQt9NjlRt|XMO~)?Pu(uIEG|Ah_jFbX zvLvx&;LNf!kIuTDy?jpp+>ug+(rv#(zbF3w_Wa88trxs6+`MRY@!TbiO9#v3$}-Cl z<*UjEE21m9E-$&+ZPUxpmj+ZcUwiUDZ9CdspsT-LI&(s4s6YZzy|U{-Erk z#l!MO%f`!(tRGc2*)>%^c6fZF*|qu36R#)rEsI;4T0>gf+m^L;wNu*PJf%PV{4D9& zkB;A-W1eTekbaT-a_-BcojRSRT^3!}Ub((%cpdb*qnq6Qu7}+-*1MrkqA#~!rN8)% z$(yPH_kl-mm%iu=A(ooB zaj)P~?7z4ZYvmahow8-$smpckZ@%kTdo7`@+Pc5^%DtxpTwR+*;W4S1`KK%IKYKeW zxIBx(0C8f`!YEW6|GO`If7b~U064}btle#L&bYt`)@M_7GnaJ=*+Cjd z$ro-4u^*eic3rO+ya!wro;6$3!c9qw!U?e)I(G4vwoY^R@P?e@mwsj0ea1!JnfG{q z;rVY^{`J1?4{qjFT$1klkoJ=Egotys%l}QbhWF@yU#01PU*++1mD5gVxJC8V#Nf2p z@Qcmp16r*b!DsDs6A0Pwc+hZm=bnX2*S+ly8m=zb{fw#O6_WJ`QR_eM!-EbMj^z&b zj-@j5ss^a+M02$8L&g2}8&|(B%^(o`>u&7b7_#=18t(6hOyo6BvC{2OShz2#CYP8y zFM8PS+q)v~*eJsmEp@Cc&Mu|xS^{AR!L8*%);io$?&dz*r{+YvnB>;n=DGzTMJsXM zN`aekDI41k*)^qRDYsT6+c(VEUbs5~z`C-{xjj@r=HHS-mKoJzK?~m26?3& zZmV^5vY+<&-^dDZPB^~s@TTaZ9n?*3*_OD_Ab)qAkbgbJyeCs5<$CcThvmbj0~y)0 z6LB^pTBs}t8T;M1#Q0rM8*VAklVqQe1FO7;#U2zas@}wd>PH_u*zo)BRGGT# z^jN@oTO;>QCwDYsU)6bL5jS~ww&V%=_VeY3(;qE9@VfMc!+j>VE~}pS;D?xZq29Z% zZI-5Y&V4<2*T}P2{%iu_DpL5A`8=zVl)uzVBClnN*YCrCS7udg#{GKXp9WqFF7>$q znU2-YE5gN2sP&_bc znR$O~U{tEF?9?XO>VoZ69DC2tJx z)o||-8#?c$t?2Y0s@Z37be$&FTTLyMArw(|Quk*sQ0}GRTKaKrP@^|?J@*W0=<7+V z4Th4Yh+DRo#<%Xt&4a4RxWNY z`Q|sCux&g&sz>EPM5STj3KhL~W+9o$F>ZRwSHIK}Pd!_4wkXyjo;Z+~@G^WEJ|%L; zdSp?2Qf<)OnHDQ!hE?K%JM#`pGoSh0GavC5Q3$`|;a7yq(0lOpK!0W#(AxB>jK<20 z{&`g=xC2L472ImbEirL?9)HS2g!}Z?oN8k#90v-PsAf z<~*UZX1B`lVCO^PecYvHjh3F)OVRrk-?sS>GYzSwzUK*_Hji~zjP>)NUdw)sO^3** zVs=Nbh{!dU>sV$KLX%pde$cskw5+PUdZU59miHD4BHMNTTA}9=rw&dEfykS-vHZ0g zai!wY*!JxY5W7DbpyTVq6Rb+OMEk}3N%|?ugBlNU9_2giFTK4XwHagl)l4~F?^|YA zM1e(S@DjCn7d_E+lDIVl3jmo9J2tYYb)gZeX%U!Gjg zMM@UeN}?@yb<&bAJbmtXqdVI|*GbtXqO`e_d)JT4&Fk~)J#lC6@m1xoMmh~0Z!i>` zmwe~67mA1cM5ViAJX|s!IKGA!x`(^EAt|Zy$tm$l-0iwNsIWH_Uw90c<}xuQylGFu zH3XWGdS$CazsjYKpvb6#?hyB#JxZyh;3qOKFPCs*`3ob%xpVkU><#Rm(;W`g-rnb1K4@HcqM!Zt8C6exF#@eppgT4)_7g}UWju5|#wFtvO~Lrh=##pJ zZ1+o;Mjz)IbcQ_->PKZ*)aWouKW^HZkale8)r6|8VumuE^x*1Q)y+(^r!QX)z39p{ zb@xC^dotNVOy8x}5WQUCqgg@9r)Mxu->Maiotv+@d@%Gc$lTKV3lP?JHPc74ajH?d>5zFe#u3R%e zc04D%dQL?7_|l~xEF1Y2b9@8fxv7(|XYmulRzM{u^&-G-yX`u&Z7@iHANFhN&!^I^R0Vr4~C2lrE}8#-m2tO zz8grcFnWH>R=7Y*Qd=r@ZpiB}LCb`@Ut6z=X1*>R&>BxuJav71=`bcIIj&Ci;!?Tt z7Xz6!bYs|75@-4-UwEOHWoFz$YAl8I&4lOO2<~iqomL4EORN-IJG_zPNj2etlczb3EHZEq!0f=Wm3zvbdzA zD=o~v>it_8Y-@JVuoi9+pXcS7rH-{Z20aD)N=AV;6uPZ44J&_?*l1QCJvi`iom9B* zdh6#2%3o`V75iXel+N&NT<|YO4i3%WKWP?_MZfICMG$T`n1pSZPB z-0N8l#6~HjfxOg1Ir&<9T^6)e?!Ad4`uci?@3{BIm@uNxb>sT=GVc{?3{-m!9jZ81 z6n1LChs7*w$W#^@tX)w!slqe7Dh&w6{dsFn?|v6vg(GNpovyfMOn4>K-#^x;*ca8GTbhtu{MICFUEr(Ors%EWJCj5)i*-kBhuGr@2ZrT_*@UtSkIj~@W$Kj{ zTRY3^d|DRrqme7cgBIQz^2@#S)6#!seaKMx1D^?|X6HXkg>lw~F5sbm~LO!uR_{tKiJ6mq#;VOpC7H3>*vu*pfWAt;d zBv6&nwZNtWJ+yW^#)BR%#7syLRP%HpUhjXI6`}l-NDA{Rx)ZFA zCTA29wyT1;tCm>eFub#H_yf^$)#-2EEq49UU-Ri>lxz!md?N4^-!2Z3#}i72ehjT% zRDP-H(rR|1>`sE4kj>g|AKg*?5dVN+?ZG)KC%X{@7+|Jm_~-z@E+s#JD8ciU4su}BU8|(VkySXO^*+Ywu$-m++@uW_v!0eoM+WA;& zELHYaEYe9yR2L5+zC`4N{&52fIGy1%MF6KUS2vG`n3wIz9Z_0LTYMm*wDOG|@2tyLO#J@?3|4wrd*?G%Ph#z%HN-u$MPcr{~qd&cm` z8lCYK9XC#MpWWB{u*l@BI0e;w=kmt%L(v7xo2stTlykF+ZjsR|q4W3^1!lEDRlmwo z5U$SjXx4zsu%Vv3&YXal`?oc&ex?=$W!B{G7g9{*BbMuRG z!hO^{N#zWhThp6Q-pq_1m6WW#1_Y@7ppHZ+l-*IPM<|(K>qBwUZ2Y))C Y$e0mcwNI+ypQ!#YifJ?XA6I$$KcojeMgRZ+ literal 0 HcmV?d00001 diff --git a/Source/Falcor/Data/Framework/Textures/PrevFrame.jpg b/Source/Falcor/Data/Framework/Textures/PrevFrame.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9a8e2a824d31aa77a95fdbd6b2a57ea4a204a657 GIT binary patch literal 19017 zcmeHvcU)7=((nnPS1A$1LTI8Wy`vC%5s^+13qlAXlmtQm0U`E+m8w`k0i_ACfr<(i zK#-<%K}1lRSdb>ckbIi}iqG{v_rCA%`~Lc#!!PH|&d%=6&d$!6nX}65VD@t7SnkIA zL6E&Yqzpk2FC++qAUJ?vP&K&07Qq~Bm{pe!M4D%<3AkEE@YtVRtFEKJ~Eu^WgVPL4PX{fG+)YLH4G%(cGfLIsj83^KGJ=HZe zH7+mA5Ac4O5Lh6t@ z93}^Gh{E8aFlINHB%E0RLjf+iVC)Mp%(~zZjDwSln}?T=UjX*kN*KfeU#MIH!8l=X z4o(g(9&TPPxS$rO6oqrjifM3}I(duBh3?bjmPojA(@b6g;rzr$OPjR+l{t@MqRVLS zH%yq$(#%^2R$C;w`dU8CBI`;vx%FYkZXbNs9R7aXN+~(}(4CZumJbtGiz{3EC*A!b z4yRr#scIXT(z8b6B9ENSx%<3*@Vh9)!2!7CWb?$s&85R;K~_VI6L1hJC(fn0FF}IE z!4v0Kqgo0+y`=q#<}L_KnD#eCZWaSPt970>0Vc8}eJ$N|$=JT1Ec|;8lzy?m?1Tj2 z>@h_l6g26YxRv5zd~4!#!98oWyzU#z5Y8RC9o7Rb+wVp)VsO(I$-`T*;qD{BA=n_U z#+ai*fhK1S8c>#nJ7e0*pR3tEu8q*!RB2>?{S|Fh&eiRwCs0|%-}M;c6O_@oEJ>cp zXY^f@rwc{&pN1Oxo;(+x*2C>dKg)y;$9=71;4_A19h!5k-qNuZDl!^nw~f2V1oRKUr-(QRP(JFnzPEVb_K@L{!GeICPcH&52M#p zM6y35Ga=^7H#0PbX6{VscXGqUCrPal`^UZxsN{jw%`)8$U0aM)=vA{K6L!t0Dv{~i)gx&o;YA4;ZcGQ)bV-cmsa+lI zt;AQ@3({4=w}D8G*QrOJZd1#TwXZs|qj`KCa);d$*t6S&%tq|>_ScM9{Msp*yo|o~ ztFt==>*~5LwOVENp{$*lP-*Lf@$%lD8HeesgG}iC5hk>h$LohZxOm|LwPl>FY+;ZAVowAxlxl(oEk7dE_-FB>HZ)JU{(y ziYF7ok2IAYX>CYoF;x4sA+M|XnMak|)~-A5=J^wEj8AnR^+FrxO?VZ!zDC>S-gs+T z(*7W(`|i+wCe%&*qN9jnzGi+FR`_M;wN%w1eKT8oPS#5TnDZ6jCBk}-uyKlP+>)Vh z+sDSmqak1VTu70@)#)91>0jMbViWB+<@vM9|%$b^MSfJyq0 zeioc@VfH@@<|KmouL6PmtFYN8DC}p=792U^XTdT!$l_-K$njU#W|%NU%U)ChTzu?S*{*}&*82AX~{h2eH=n=Vcb;i~JMb zIV_0G#yjo~G9f))py!XkV#q|2g*VxoH3K%2jyT7#1+8GgEL!knu?=m;`ptFomtkFj zlk%rwT`)m&ct;Y(WGkx&_O%21n=*KCwm{QX4eVN4+hc9GMU%dl*{JI;J%98>o~j2$G(FbjIE`>rDI+E^P2>q*|d{vtPP~*=Lc$nz#X_= z;DXI#uB4s+iP2=^J;8l}Mu&}IY5qKh-RCqYL#&+zIQ+u}2kz$oi#oO}cv*cf>_ZL{ zurI-HUT&*c5Zhs#+qcrJBHS9>=D24SA(l(d{_(Nx1r~~ctbpC{hJt~`V9DiQuf@29 z*1u6LCRnKWWhx*-co55?v8*e%4>6qJ8}^r-iXpT7YPKO}srOGE0?5DUAY}GSS6Qq; z&c9TVRpt&0!eg*u?m?Sbn+f*YI9%*HfEEKk9(KXjV$oPUI1({D8H!*q|U+>~6Acn2oFb<^_|*yMX?Un9rX`iZ%_x;}#?( zGB1eVvjQ}Mwh##{ zS@V&JA>dUUhFxr<_=A8k`xmUB58L?t3tj*$vHxGm+52w|YasBh1?r3%Wy5oOO@a-x z15P#_ivr>@0yoy?P@n&ufuQ9qyeuRI0B5p3M6C>{#b^YZK>ycBObPo1V&)? z3Xl4e$svYnYJ@OVZ&rFx6+;YG+wC2qrlG2?25m&`4)MkWV#!D!EbtAC#@RdUuBRB3g+#MVej3s*`cLxU%!VGsC$+0^( z1TYJ&CWmB`kOPh6*l9bYhrJ`xoJhhVbyRg#FdEvLNIe5p4PA8uP3`qakebv}Q`b|| z&{9#?FjUty)G|QMUvgkHlCPhkvxU|CSfFGiH$SP!$Vk;lEma~3r>0?GV4$Y1sivu^ z0w`3%q6lQ~-717I`Gp=VuwfVyo*msGSv`WtC7f&|2RNOZLU72>ZvQB&1wvoUPr4A` zps)w`#i(I}u)$aYISj~EgEjqyPVMb~lK-*W;NYLsVPwk)z|r3=SQyK1P;3&0cv{qYy)L64o2UajqbSTUe-HNrlDP<3;3H5cOe=aLoahMMXiudJf3Z>X+5M`{nWz!!vvi-|Q=)U{j}a?igJ6MgZ1QU8(D7h~v0 zBn5i|8R3Jyaags05G;;8oV~rFEg_8TO~7DnEsW&A)Ku|!Uqc;r4L?m?eGL^o9X%Zt zJq>Mb6$5oGZxwA{18protd5?pw)R4M3nC_*wSWuleL;J5j4#FjFSwUnCs|jTj-dX${~NXX1kI0L|?E-T8r_v=H?(N;)f3c z6=5KufV8zV*VQo4)m70{)nM%sAT}UK8+;g0iKzK~=YkELhl21(wt^aZV^|pyBRLF9 zd9c26^Jx4Z7T~`V;9R$nAg=#gwVERfBl?jey-8S899XRXptNfLj`c9_h`(oC*GE@R z*8nWFz9t5%qN~ALd0jmK`Kjw0sAIL&bqq8YCjKYJwX_X^vd!;@|2gCHBE|T76L45y zjMe1+_fz@rHo?EH!5??)|E;O0u`KYMZmIoM?bvmTtTk9fHVV7~Sm4ihkMY7g;-BX~ z68J{~|485;3H&30eiMDjF%Bt9RAltH@Q8xs~{?_$OJEC~Ux7|C*vS@8S?LiMS(xvvS$ zDmIQtmzNjkwI88S;Y~g6ue2kAm9%U@g_t;(A=C^tst>~2|d{8x!d1XE~5K8 zkhQjR_2W+LHG?g!`-S_}@)wSn3BEajZ)@-~zi>Wj5Ontt1W9%N!YNz^8JmL;RMNO; zK1*5pvM3#a$6{1j0{waYM}nV}{~lO0o+@j+pJ5x)V(vo(lBLxc@L?gGgbV{29V}Ai zuS)#CZn%i8MSQGx#`YkFj963BGt%Y>31YehV%a_3*!Jwx|adIH%VD z5YJ{n5?-p%l12##{<#j~6c>Qt+tWb}?B}{|=5q(BH3&k>49@92fI&U`{@ab?G$?X} z;n^RzAaiF|Bqp2`F-OA5dT@a+vLcWKva;45S#vRZuPT5^9CIpm$I|Gy;u6 zQ_v4^V2lSQ1QUlXgUP@YVe4R;FnyRY%o4U4<_y~k+Xcgc19suCJ+K3?tZcN`-eli)m`0DLK27On! zQuuRtGyEO=6P(7$%_+`_kmvfYp!6n4C zf@>X@A(t(e2N#YjlIt*6D%UlxVy;TAR<3@oDQ<4=rQAx~dfYbLTe$9tR$89x~5io;058Jmox1JpDY=yaK#3yqdgLyq>%S-u=9( zy!pIkyiL4=yg&Gs@G0^c@;UJN@`UV(E0Hw3B$dIe~LVuH$o=7QS=!v#+X<_JC(d@VRGBqX$2 z$VA9fh%9tUC{O5_&|9HtVR2zqVOwEeVT$kt;S%8{;V&Y5B8noWB0EK*MN&m>i!_RS zTEe$v^%C<^Tv&2{N&AvXQE^cXQ3ugr(G#NiqBWvJVmxB2#Vo~q#SV&Pi9Hj0 zFAf)%7dIEjh#wHo7OxcVm*AFAlCYHskT@<;DA6D>iV#C+Bis?Y5f>1T5bu`4m#$uF zyEJI&>7}=pwk@5JTq$WPiIqGiStQvc`CV$cl!+8pDqe~z)hac!47tp5S>UpSWp|f# zOLIyqOFKzNN~cR#N`G1|v3$dF%<}lx&!g)o^imVlND<)U2T4}v9WM$gQ zXDdIgTDr<)Rlus`Rb{J&WyEESW$-e|GUYNKkqD$IG6;DNS%DmtT_I~D8!mfAwn6rX zoRXZI+;GyuQ4je6svg`B4QK1$%`(3Iz(CihPRtivEgc6`w19U#+wnz52-N zyQ_zkq?BxxqLm7i-mDQ?gIW{1CUZ^8TJE*_YXjF_TKiI&Ls>`JU-^P^<2sIYy6f=k zF0FgDo@>3qdcyk5_3bKxDyAwCDg`RNs!LTjtL|65tNKMvLCsSwLG8I3LtRHbNIhG< zOG8X!lg56HQjIZ9WlfCc1`hmyMlGf=#>aN?VL=p6%!+ zy-l&3D(nR9T8<#!_dIz#-94{)PHwZ@ma?sXyUzB*+uL?1?byAeW~cPdpq-DrM7+GcZtvpSh2E9F zi|*~t+Bi$o!?-t!n*j*Q;9Ca*eFxo2mY7A!#Cg$lL z***LA^v0UTUWtRn`NTb?$Wsnd`uAGz&E3bpFK}P|e)au{`zH>d50oBUd2sK+_lK+x z6&w~mOgh|tWW$k+qntT&uB%!wB#HBO#8$vowEs^PTm z>GU()X9#E75>N@biK2DpM7_3^SOIz3TbE3 z=;!^nkOht20xxxU(X%2D06= zpI_C#ns-h5+VN|%IfR^dxz4#2d3t$y`782I7jP7W7YtwDe*I;kMd7_7m7*)urPSj$ zpc~;gKHl7Qv+b7Mt*5v3Zx`KBymPTwyg0rDRuWY*de{H%yL(&jy}G~Y{?pP8rNs|a zALKlgeR$y!;?e0c{<4E*%<|~+$;Tm&KRv-cdH>YwY1cE4XDt;@6%CbkmDN>NRnMNA zJ}<8}s(x5wSabh{{)@Y{y0s;BI(5bMI`zd3x(y|bdX4v98oVrhweeM16RPP+vqf`N zi)~A7>z3A6ZEkHH?c3Ymb@+A+y$*W)tuvzYN7vprTyNsLmvpDTUGX-vXKfGlo$k9w zy%xQ7eJ*`n?=kO3K7@Us_a7V(8b}?K8O$5f9C|oxHT?49wvYWIAtOIN9sVr(`Qn!~ zU+#Q0`C2#XG1~u)^o==oVtmC>Lipl1euMACWa z=VsT=mNM)ZZ*C_(Vqe_cJUqNSJluSI z{QTgk`tKJE4u^Aba`AC<^9k|u@e7Htj-ZQxgY4j%e+bT%esA})#AF{quZF7OFmPu3 z*Aet(;0QW!YayP+C(t2IE;t7_44OZJ4tQN$3%=ejI)cstSOX^-xOw2<#t{bNfQxd1 zvmRm^rcT`A-rx+ngl57O9>m-c@LsK@KBEex{ofSLwOtZ3Z(+io>R2cxW!=`5boC`a zYwF`&eSnW&YT3bLORMZV?#*s%q{rTmvyP!-!w;p*9Yy~)C(-q7Jp6YbJ(qj0x?^a1 zt%2=UeAKbDy!$n;hiS@&n>+)ef{{Ru8{K>7O;3nFq zsB*`DD3&u{{!J6NWqyY_HSLuvj^Fn~iabYP&c;QIU5&rJeWcCl;19PHN|AC-CT--! zPM>tU;N?5-l-)c2`Pqv2ZC{F%YeQMXJC42SdNMmPEICsl=!R>*K7DwmW&5@(A2eLe zZru~i)=WQ0jHd!7%bBnKJsX2+jVM}8PpwFG@T!=vIwMZYC#`g6TsKRjcj-zLbrjG> znb5_POeo_b!!OP!P*QiN?R|<0Ekf77@9W_*PS85q#0xofvRY>YOK zrM@j;LZ_5#%2iN<>zI(%o?`>{D9=0NT#Xx|Qr>8vFoy!o)|DwUawvlm<%0?n4vkEx zc6OcnOhqW|u)dSd1``v-$K8*=S9*E$Q7bJPA=3?_K_s)>$1 z>fqf5CN%y{tSZ4-Gd~HmO6@t{7NLUF(r` z?fJ1Uf$>MiAJA^o+6Oc0XQRJvA9ufLG|{f9HR`t!oiM}`7CH7Z@vf`<66YS^Q$e$cU0y2sgf zw9n^piPnBT0k-=2=OUILXuhUA#e}MfMkA|i#@mu|@sh;gaK#G$WkQBGD!pFgKKR|t z4=hiO^JvwH+T4CNBH`hw=r37_=qY9J4;z~QH2oEm`C4 zI1+VOnLz@o@kuhD5xnFW3`xi;QI5IcoE}9Qvl9xldjPgf*mC|D^-8Z!D!s4h z0Ru@tGp6%xr!aZQt^LiKN-s;W!YzNDq5FpHfJS*vON8I*y@GC|6neiJeNeCe>j&;j zDk+cNc($jlwp!w8^6{xIoZxD#UqHJGHpxNoyG5_1Pq%$3(y0#!Z`yn!Si6OEGe7CZ zW&MKw)jk!eI!bx^`EM4?hgV_w_(%b{j9znC)+%wekV6iF;^cHnLvEt)a_ln%ML%PF zvU+_|eWnGmN4?<0rmk;GCM!KFU8rgntJ@~3Pd&9e+Wyr$zWg?o_UwUp4#S#eC4z3{ zXwWpu&qBmHfqmcl`^2sX)|C;)z9y4tlPL9%ZJpSwrILxZA6z?#Z~CauGHCTE8ZEZe zH1-4Kg+SBwH&-6EWd%19_nvT@))}B0MjdbPJTa46p4XWBrlG&n_TtSvov-mXH?pSV zw!3ifVuGOk+LdPeDelVIFRCj&oX+|PE8hk7ayA^O@g}1OaUnacjqG>>B@cxMj8S<+tR0wN+oJhdG>0`K_I#$j2bM^Q7Dki3{5HTC({@?1X8H0M&TvgF7vkZ2 z1vE0~DjWQ&#(M_Q7j*PW+KVoaeb=+wpnWDkI=riw`&>cqaL)POPTOq!j+0x@wIs#o z_7zobgDxFbo)oF|dU0BpfeUPlR^4~CF{E7>UYENmSBYe=!9N@L5;e$O2-KXSrX!;f zwZFwKUqCm+*Yg{}c=}SxbmhQNF6z1P)ZI^i*ww$&SU*H4@k z_Qq7F8 zaJ)jeTmewZY=>$GTBR2<&S}VSk9(=&-C_MKFZYrCW2@7(D_v28%7ul66qU&eOOIsF zYKSA_)*cP?x9-8qJQu>>-FZJ*vpb=;y6?IjORum%dmKHx?P&E1H4zEs5?(7*S_^uC zg~0xhM^JP##=m!4*_}oWyWjEJB4r|II16Tr0u3(=DV)S@M!r0Lbcpg=&}}xI2{o+R z(#)`pvbY^~HQ796t=B;xYCe$P!uxwvt>HASI-{0aW3+U{XUd<1Es@jY=O7P^Uj^L!JHD=rt`K zZ;3jcu@TzXTIkqz*I8y76EBqxtn18ICS-iE`LhDOwX`hVda~C<&a^3gx?tvj3~s2> z3s`OXYxYkV%X%-Arwn=mx1VH3PfsTlu~!<+&muO zGG(PalZ2qKU5>mOIg67Dr{paVIE)GOO$ia_N@?(7LXFiFHI(x8aLQ`Nj)2+$kqwm| zeh*7vp9|jvp)dJ+#01BeUnJING@^{-h=Gxw&(G@=9Dja~^!9>5AM6uXDEfq$ZQh%{ zLf`_SGqq3T#&13Mat*Lx*D11;LCHPuY1DjA^=7~04mi*1>PXH$>d*y7Xc8mjL9I;c z)Kc`>&OOB4@!7X?3!|rfsITmVjN7;DQ~B)Uk!1q8Nestl0pE_ASU#vKYeg?^yHsgX zLTL#_e`_BcD;)S7lKIW6(AIRKujH8qAnRzPO$=PpX2i7=Io-RiE%Y_M{95I-kLQ#Z zP{Rujv>#RloarHVZl6lejLqsn#H|H9yELj)-wZ5uJvrq(pxL8pj-cSZ#*f_{4UkDJ zvp>(pU+#ERItIL%?0i_h$8cDfP`23iExmHb>^7`=>_dG@q)k7k50a;(30X8dTFmaL zNa`AUwCkO;M3eHG8s40HMcUNCDhA`LbLsi9sopOHg4Dxio659fItoegm)VZ1OQED` zYw?fjAacQ4!WX4oxe0k2GE$XiBY+_R|4J|;A|O2|R7z2>`{%VykqVa*B}VEOf1K^%oTKXFTsa?y?sZr z%mg0Qr-Yy^-x)=5sgHi|oKl)ZoU%sv>;``HXEpkap@;Rw@Z3vMFE*tRS}MJoay;k( zz*Yn0%DB~ODVY(B6-|j;e)p6@XZbSV`sP*by)s#-;b3+Dgv_*TeEA(Bt-?+gOOyFf zu~NE9&NxM?VEB_8a9mty4CU|Ev_%M4^-4P;Pr%8#DeeqP+HWDLy!y7)e( zzHVDx^nHxWWSTPN^$NFBfY$3aWqeq4&z|uDsb22R0~eEl9$lUM0q+M&BtrYq+C_Qs zr0LT^Z~0S8Du=gN{OEhw6MwGYrfaihQd4V?LhcHjxeEh$a-FB?eMZ#}=m*ERo;D@o zZ@fI<7$@3;P`mmK4V;{-1G7pKWhcV}xT2~apFUUV^`e|UNc~`(^Pzm>$1=>_eVxx7 z)2^O5gk0+riXOPhDJJ67jtFGi_!L@L!%m;E!Z+yix;Z)*Z>nS8eIWOgD9`Q&Qv^$$ zaVe0oMK{iqUQMLpHNKrxQBUNSR^y@VV^S?X3!Ohl+d^D7W<>t@`&ZmAd0t^+=vY`f!?c3!)1!;9OVYc0CscLV2z3L0AD zYd(5)DjJYdbicjvp?*d)Lb=!U0_LOXcTTPIyW`e`yl&t2FcRgk-EE?Q<@UDzm@?UA zir~qu^il-;+fW*`hA5ZfJj-tEb|NaCIQr(~NEd>lSDsE8-&da2lY0Kt)s51d+oOHA z)Jwd&f-H=^iGJ!?b}Y~TtXg1hXHBrO7=LL(^I&}TG;7j@`>ozJohx{YQYs&mLupbJ zCp~mT){L|-O~}ei?J!vLZq=b^A8HSZR(YB>!;`vaX3BUg{e|dIXO^->X@a(O@L`5j z!DOTP`08T=cKXzb9h-#AI=T`dnSAAHp&?K9%}=))=Fk~)sBWs3M&X{Fa*smct0 z%HY@fo;$GiVKJ3f@guDWC5KWwI58>P z^zKA6JsI+$W&!n^bJc8$sjB{E-o=TH=;q!>`%T;uEqE~TQ-xWv} zpI01pYI&6$za-R+?UB0fDD1CKt}(h^FZk+=+siYiGV9&GKc~OP(FY?3&>!CB-~+=8 z>~n{_Q~Zk4YK7&W-Yb|9q3;D6@s|F8-gBq;aID!%luL@M)Q-n_`YK)_o;y2x%#u2{ zBvtg~4+W?f^g7TQ4@J;3+Bdm6caJzs0-x5j*l;#(>`^R^+7o{n*ePn)Y1*XUCCxz1 zZdv79`njKvp(pksVzsGbC*0^D;%$6;IR&MyecwM;sbu+7kun_zRwBoEXKvfXQ!a-# z`5gKZu&4FteX?g5Ry4JiA68W=M`nO;zXABZ3$EQO*gj6zRauDLUk4;V>D>&+n?q`ZKu$xWN0+i z^2@f^%TX^r7pWYPn9L6N++3#UMW!^^fPG^Yg&H>>Lv7EnFF!VrT7UGUhq&{-)^)T! zEw*~!51wzs^$hFhn)DT(x>xZ1IdFyjuEd&mA6E8yX*QcOdb!f8evr<9yEgSSM6Wz3 zgP6IBGlnDL6oAd#%9GNec1}Cd(&YYW4BPmj>3uQJl|GzJTBXA;bRryn6>R>ARMuWz zaN|JcCSSU*?d?Y`M8#TVJ-1!L%F{#!$%_&4yr%r(l(+-e3Ek4O;#HO=de@Zce00zh zy2wA?-k*H;Y-f1#U3c}wfP9rmddm9P zfvU=7k%AiT;W%2#K&_GH4p|-B^HpwrFH;p<5W8*}By?JJUu*%vY+hd>wNt9#`&UM6 z;PU*c$1P&QYnP)aNuQ{-5~vF@v>){IB-*%nyy6KjshYICS{xyl?SSo8 z@IC0V(aq<;XFsXBzT%VuKqRubKRwmVgGVZm$z|7 zK}Thn#0%Fk$LKz4jTfz^k@h_zVPIR}?6wz6Z4pldd*z)xCqKuZp)1qdfh+>Y2m4dH zK@1OUKKfWk*t?imsmsQs)MdL!iB+#|e3WvL$Ya0zSb4Jh#!d8Z=m}BaXx_Y$ZjHx9Kh*NNl?GKGi+V zS(dVy#dYDRPaxa}FOosAUGeQ>$7^qx(G!BRa_GF3UXFiC(zt7`%R@W9h(PvH#preP zLt|}j%tOztbz*GD7NSsOY`9L?puPf|&$11%}8V8P{ z&h5Db!iYlrSf`x;f)HTskgoh}W`2I#Gw_r)se+l6paYjTb!|jGuY<5M?W;c?#Fw9! zq0vq6Q|e_@@8o7meb#?UhcIvGJGLm(y@8J00{^$cWs1&Fe$1QaOh`mA`p8`cs24Ba$*W z%GlD19p^c_ry+m*wY6k6uS=E27x2z{y4|fb_N{(_F8z^bRJoa-C$+6_n7uzE>F*C` ztUTSXm;8c1ddkW8?MDstgFmHT7V@gcqH=0>?ifFroa*`Ig3u?@TF>ge2~Jz43%>O* zNI8tyEZjgE0VEe{r=2@-{R%hM$QU0$#4TZXGpwRUs-*|AvIC@5YGci;tj<}zu@?!x ziOzT(rBxT%#QS;Tes_VHzRYlZSu#BtsKM6p@&ToDpVm_>8&{BCTuD2#7lvBz;_%Un z5xQ+mtbJ@lbxEfis`-n&OUND}4fBsD+-R>sw6~0&-BPYuzWS>|lk6mcT;^hvU+i<) zwHZNC_AKvbLgywPoISN;b-(;1e?c-b2p?bYgcdenm#r3<;}DX(VpCOD(u5DSNrv`J zjrOzNxWE*R97d`(%V`D_yXQEdZ?|4A z(*7~Mw6Ce3-(Ea^zsSye1p{jOlm^*2Ct98fMXjsrjP3Li;cI15`H~>1Fn**bC4I*V zwAFrJhP!Ee*^x170{#AnE+_mI{Hv{&Z>%oIU3WVRG|%g|l+L^#{r5TdUvI+yL0dwY&fT literal 0 HcmV?d00001 diff --git a/Source/Falcor/Data/Framework/Textures/Rewind.jpg b/Source/Falcor/Data/Framework/Textures/Rewind.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4d507a62dbad4bfc3665864af113911e60d9a9aa GIT binary patch literal 18715 zcmeIZc|6qL_c;C>V_%YGkdn!gE&Cc}Un>!YXqgy;$uf+w7SpPdR4R%}LPF7Ik0~XT z2%%CbJEdeO!jJ1r6wbp_fAs^g z`uBctAQtLCXmBjn&E-B{9b^xlKqB)`)+^Q+B*^Xo*2)n=u)aV`*g42|A56#Uh7Li3 zyu7@8yn=juf+7O^0wO43K|x`Zl$aPwOiW5dkez-$<}?3zM+gZB2rUs>x@5^x@g+-^ zh{Lxf;`6VF{3jK#9zY_zkQStcM94uLA_$}ig7pq`63KduFaun2A=n8p3@0Rn;Naxq z=Hcbz7XX9*XCVUOKrR$6g%F$wBnKx47Y{ct7gA6c6pA1@SBYwKS#0qZlMCCg!!3R~ z>$au50?Or~kFFl+K&usxVv6hd$8WfBeTl1g4z9L7d~1J|9U3QmkB2xNyC1irg>*JgfC<+W-^U zlD;+=12VpMk%j-61EpUqu--s|NOqedkQp>qQyv#uJClE8v#q0ML%h{9)q*>S?5O%G=Oki5i#PKwRs*~^?r%d9BTC>vETX`fQc z?GEqW5me9hHB2Y~s(L#lyL&^dURvgyHw#*^UNX-sYzAXj;MFdC&!jzv;_|N4^y_Dj z5y~TtXW0=OD$DF2kDj~N*;JVkQtM9${h=FG7Fi!p+CWq&$KI6f^S??y@Hyl>k6V+p zXuXXfv!pMo;A{UD7DRL}t;vb=r*y_A7@s(ss1aOzty3@M-SanNP4B+E8Ql8#EDK`2 z$ef-q%Np*Zd&-`X346C(@>5io>d0_(+Lwn9aCX;*5=U;PM0N)*`&vCqHEFNucm?{W zIcRJ)HDRVGkivp4upm!ktiywuuC|r4yCbjx5ob4s)Egi9y6e(p5DVgO8IR9W;>GXv3vp?J61Qc%ZNH?7LdF5#O}z~F!dv~?t)Y_Q;9#v;7Sy+f1(j~Cq<+(7LC4h0#s{cV z;za@lEGTL3mx?K`FULDZB?i(uXTsGIz6gefC)ilMriOfN!>}MAfrktivn6R%GuJ?V1w^S-&^BzVMXhtP{S-BK z7r}z=OuQc{?|eRG_T$p`xQ?JIdIYtHDRMb`4%0TbZ8#zhJAA%cuF!F;E^vRzBSQG) zp&htVq3-3QTgQ^#-fMGkQkp7eY@5`)fq$(oyK7WqNF(*YN`L0d)-}OzS|{p@`6rxZ&_B~zl6>7TA!J*5mmqj-7n5a>FZ!?WF5y9B zHr_cp$ei?Gfu27Sk0TRF*4|`qxCd+|o&BA|7qkL~VOsEn*@iaZ{pPFrhi2V?-SSt> zy5fT7@y;ZixhGr$J1xbLh@l<7q|ePEQw??F*t}A;{Wp%!G#i- zh2QW(3wRfTf57hr0t*Fzw?%q*t{}(ec|fcIPq4u+JlXSTHM?xd&ql$+Y#%hQZ$fMw zY{_}CDVHtgSF&rRvGaWFJGdd(ZviV0PW(S72|%-Hr`T9~NYl>`lmvl0u+2b1{KRBk zNMFS0u<>5tzCfeT#=x5Y6T_}^8ss5(X0iSPzdA;a6hLFOtP4M$1S|KSO^2Z z6D$<`S|p&5h#+_^gN=1=A7Vs^Z}>mVQ5;ztl)*y{!HT`8Js|%N+6P(w(o&ce$mN$3 z!g=oSAOa2_?jE!Wo<@j2+TmiC0kkN%c-R>S>)+bq@h1@@LjOQ<6G;Sr!r}^P;|kaA z#KwU-guEljL_2&4p5#r&!@`5yVnP=dc>Z}}7z39$1pA}GUA8={Mnu%-3&ew^#sPfi@FEU|I8# ziJ@Q<4#)q#I`IdAW$d4@ff0ATAP|2bb2*>l6enhw<)^6!6vl_T~Qe<^J~N{`Tem_T~P6^yS#A^eV8(Lcse0 zt1rC#3PTpaxFJG5z|=tl2Ppu+WN?MEp-?~}`fn)EkQMmBi#fagc}t3iZBc<1SRaK} z1dz$0#+sTT;Tqm>d{F~O4AzYH4%O7w(9(p~nMH?s;{x$yv=1KG6YJ%^R#D~91mE>? z9{P@2j-gig0D^ri3GW)a*$o#Ph+FF`XJ(3C7i}CJ92$%#d!wU+gF?cMqu0x^D>nu( zjMkJxvq{K->*d%{K=f8eXS5ZOgh%UZ7^vg4^>omNYc;eDwASkAtwDp>rlF>mp{BO3 zI*4#;85rxXMgL54pf!@OpRtRz?a#JAZoS;kPDMpUX+-I25J~=;+H2RY)zs3_)X`A~ z6zbtIA!P4p^^kD+g&M5!;W!e3?F*ve8iAh^L0&HhIGyi8aOh&Se<`a4LSNh>T_`YW z*p2(*H1R?BV0;KU9LQB0?*2lhj*g4ue_3sC@FI0M*(MTj^sfdi9JVzyUGU+=2oer& z6A4M;?AD)YyuI_Xb{{8}RrR2I^P1;rH+~% z@D5g1APC||2m%G+Ab@~&u(2}GUTa{WuA`w1>pAE+5QRM<91LpA&za+j5BmuP5zuTE zGxo;85s~$BIC%K+zH&d&g#TKE^VLQHKmK1T#ynX#(T^PEO~PCF19ATwwbKNwYr?a2 zz89MR*g#=94r?wxVTQ4F03biKmV4%-xBy+0)I>3ZwdS@f&Ygj@XOP|hXD5` z3ivUsUcs%vq{2G8IN3UEvSGVqg7%vTAw-U)5EKl)-@rE3R!=W9PcsM-^8v34goC|t z;h~PMw(tip2-sg}*bsx^p9#2i-#h06&+Y2=&S>;P!GHJ^!G)5*ha7}6px5;UJ_~>k z0XQg%917#d0W9tl2*Vt3R8O1)7zD5s4ExW+@b?UHpLy6EhJAxWKp2<<<~h_i*cXN$ z1NdlU1RNelZUS&ZBmo}<;Fkba2#N?M02uy~ARdhO1|e&9+)#mx#{~db3*ZGwZZ5#_ zdwIZ^d|WN=P)?b zJ0u2z=KBo41&RKXk;_KU-~MIcZ*>0(gvWNioVJBMW(bL2r4@aK~isi;S{a_@BR=3mA3rW9|>4resf(3c$@|-(8cs`2^J~;XW+N?G~o6Y z9R#%Xe4rZ*Yc&o8t|Lh3aNzyp(dz%G#Q*h+zp?cjA8TCjes~fdcxPyLpk?4Q9~kZs zUjpofpa~&=xG}%g@L#_6TMaNcuh#$&%V9#|SPf`ti#UWFYl1k%1R&)0%b*0YIBuKx z+(8%yf;?o0=Jg)HpqzdG&%k-H4yYUY01ZH&pmFFsGz-ou@gRf{Vu)o3S%e}&4WWZDLYN|K5StJ# z2v5W=gg+t#5rNo?IEXloNI|3{vJm-*+lUIpLqsE@74aJJ5ix?8K+JF;IRrVxIb=9i zb7*oHaaeF{_K3bZ~s&_{1>PRD`719amh4e#`knzYwWGeD1vItp;tVgyZKOjFN>73l0Vw`ABbxspbdrk}|jx&ri zo->Ivo%1?pDd%I(cFvES@Ye9Q@ec9M@-5|4-+pfG#L1 zs48eBxLq(pFj+89@PXiKL7I?|&}t!bAul1aP_j_I&?BMuLeopcmS`+-SmL{cvLs_k z>5{f3UxfLD6@@K?cM8V}rwZQ{ZV~>xlyB+krB+M5m&Py6SX!~PYw47Tn25HBlSr^g zl1PC_qsXu*kLYSq8&O};L!#NDk3{>#kYe&;R$@4@gJL;ibz%eJ+~P{&4&uATkBi?F zZx$a%iK6sS?x<)~2C5qMK>{hUTEamhNaCc#9f?kf8A%yQ3rW1>G07syHpw4S%cabv z@KT9V#ZnzoGt1D+Y?cKsJH4!I**j@YX;tYh(oxcvrR$_WFBe~KvK+TOarvF)Z&q-u zP+j4&V$X`~6-_IqWLC=9$%M*Wka;BYd8Nck^Od_-o?Th9azs{4)>M`tdsdbzJBmi3 zEzm*e^XSLu@l`8U*{_ONm9?sQ)vTP797gVdT(R6cd472#c|ZBH^0o5g3bG203VRg_ z72YWFDHWY?UGvS8m={K*MzLOx~5BAP~Ad3QoT_9qlSdWCXE9c zWg1^J6*RpxPixj|GPU%zg0yn9y0t~MH)tQwuGF5;QPsieWazxq71FiUrRbLHe$!La z!|Pqvd!;X?@2G!7zgBmt8 zx7%a)z@E!~qy1_7E(aM0oI}3D_y)rbaT^|Q6x`^#F@0n2Ce=-(O_h!ujvE}$IKJJi zxH)8VnG@vX;B?06owJg2m~+(@?k&z+GPVr5XuIrnsdp7~#kv-_es{BUJL%SqQN~1I zYTbq1x4Y-N|M0N(Nb&fvRdZ|H)@Pp5o&?WwFCH&308!3!nfV_yEAwH2y_a}3uFa(1QiGK2JZ^42oVhl z40%jkNsK18hN^`ognkG!4m%e%PTEMi7LEwt5nfIf2gX!WgmT2;h>ww`kr|QGQSMQ9 zqeY@aqMKq=V~)iP#oET^?%~{n+f%!D)!zMkKgLkJG%6XJC zh|-wS@v?xj59OZatrZ(8YAa1DOR6-g^6sy?pHYpfK3T(GbEt+zjipXK2z~JRq5s3a zT5N6iqpgo#KHl=Uxo%@!!xP&lkLoSzsSWEJ?l&4YRy;L&TK3G~S!t7gQ^|Av=OxVs z&801dE#)uPzNl!8=)-bB8c z?cVp6>uut@rSDSTuXum8N2RCugTaUDkJcZXdR=?F`*3}q`osGf1BV8M22+P*hw_JY zhVPHqj=UJ%HahSr^waF;gfWq^i(iz#-1}<&wP}3o_`o;PH`YWFZ5b_ZQg5<$%6aPj z_u%idKN6>xO<$)Q(d%bCW`<^?7(9&gb1HL{%#F;qtYC1q=GWadY$V@bd6*^YQWXgHzIfCIk|R`N=VjpZGcaqxL3cO-fSowGkR03y=;!HY!0Yd& z;Op>jr=K|hYv7AHHxClrIKmMeND)qm3mkv8*upL54URvH>zvNwLCv2&{-`VAGp;~7 z@J-Q5&o$-h9b9;=zO~Ys?7Ie%ZocG4ZN0p!5AyL#Ejx79#y01kdpky1dZLd8pMb_k z96mRH3i^LI1Z`x$H6Z%v`RnBkuZE{p);f3+Vvb$NuV{QdLRU53;I%t8F}6_7+ z`P0%!E+9^BPF^H3nEkaIKCo@UwFPj@9k&0pPS)*l@rSJ+}*j=iZ+z+x~WP`}c#} zfAIV-SpM^X^(;NH3KgmP8S|>}wuetq-Hv~grLtbIU-4=xP<^cY&8UDQc-!WKv)$!g z-KXk2xY*T;msjwi0E|lGWP+t{_!fFLI-gD$~vPi61%7QNDf-9@tO!ekq`-dvG zDvbX0^}tP*vZt84SWxEvz`NKD@-MONf&{yh*av@G`F6VH=>}`(;0dU@Uma_iGkii|=0p1I>bJJPl zkt%zf7=2THp7Fcsn=zaHKea;C&k3r`E~>`GXZ=)7dmDk**7hvON83GcL8%J3Ke^tz zv!bY|_Ymdm1D`_mh+u=swzluf8iz$5*14|urG@wGIdWb^U&JH?QqNzupJ=>RH5g~& z=zZHE&&FhrSYpu&GcCrM!Ne2DrkTj=PXk=|T2DCLGAh(zD=>xC0e2$3@!Gj~f|uoN zoKzv58z{+4RdOHg$>vZNbyrkSkSXt z;WO**+d1XiupkjBOgdYYZ?|aE@0w5KCcR0h$hYzi=1+|}SNqvCx^UtvlcdCq52o+w z58PpAJF%(Hy%Xi7S8mB)7-CR|J-IR*4BcU72>tS4)SSAWwuxxs4I6~%D-~B3)F@1E zAvRGp=`@QB_m07;_7hLlAB(Q-#iS>ce6Kw+WzlbDu$A8P)JyilB;93OoLA@CNAe`- z&H)>{{dH@oKPT@~iVk({{eyH&&h}AeP*?B`?=qK7_<9|?-R|Xu<5f&j88g1H@Y7Ok z%(e-9#>V}xvOiJ3yg^cBFRL=*XxJgALEB48pucvlUwj9}f zr_80zQSGp}>^clx5%gG}1({h+6rX-GYtE>Qo{Dooo!Y6=f3Y&iR+S;l=mESPXEZQ+ zJ2gu*bpp$DEMits@{cWVM~!>6qoP+S9$Vv}ly6kHwIFA8%9FiP@%J%v(Ug{Z`gaRi zP_u4KtX!Kep}i3?^dd<3(O8Y@950vrZ%}c|mZQ`0 zNiK}fUF#YP=?6wPR=a%PmhY=@*F>yqC{dI>_1)?n(7O!|SMo_v z>waAF^VNyGXQ=o@2RZ8#3dtf%F&9oiNRAplAg+^OxDUqLGcD_4avE=()QQQx*>4aV zf4ASb{jS7c1S90LwZ%)NZ>F+S3iF70g` ztg0y1zqxr&qn}TcFDaJs&0Photo}PUFv@5r2f1^((+1*1hg`}p9mr4Car1cGTbxJL zr3~?bIYD=zlWSsWhg#pPd%Gz%faesaM|fh5l8LA`W)ES#O79u^R@r(K#jb~TM3#1j za*lGEN=uf1zLM1Cljz22bxl6M{KAX4LmOURRGmJ~B;_&VPts4*3nMg+&|M=U`mrkK z->VEJeXGhmouyd+T73LL@zdaqZZ9K_cb~9xyPwXS8EHzBbghVr1am6C&KchS?#6G4 zw_@VrBVrD;n@{XK7#96Q1ND_(D?y7wxO}!76$k!5wN{n|87&{wq}OXcol|Ny#dUPj zysFDZ-o2_dixR!n9bRFCq4({_$Q2jL7*9wQ&I|)>z}rw{8GV}%bk!bkcz2Tpy?k@N zaMEgioY=NT55C3@MOluejX%DOV!d*_WIr+cz% zKE?aqPpr3VAFM9Fa6r!XaGBW}%uE*GY={M!KBlz{O5N&Yj6Ab#!}lq5yxQ_Q_D3DI zS~kUumMEKQcE;Ey_wvcC?&{CVoM|Ve)7BWLpH9RsXF)9;AGT>WMn-4zAFd6m*3L0Y zlYiNJ)8~4H=2yQ1CIpS#J<*iPeYZbQSGA)o!T#ggqe&0kPP5J8zHnva?r2om5>X?x zx`f=Jt5! z5s4YQl)WW{TpJE9oqDOk$NX+(u8CAl%Fx%F#_^uR4QYr~F-#-M`QGBegDAJet6LjB zHTV>N+QW=HuJ|~UmMm?a?&Q4Y`N&rO;_6orMAw8}vEE{X5r=#Vzj^c(Gv-Dc6&bm0A#qx5 zHe}_gEY$bf;z6S+Bh+5A_w~$Mb=WR5#;j>CsCC7pb+YNovxjtDE|%6|uNr|V!Zrc3 zh({%&FnTM`xXnowPCo}r-eBT;O?pG|`natrCnK92^!1CT3;P&s+clAvfaeOPPmKg}}H0lRYbBB7U=O0$NQv3BxFI_}()ZhB;q&+wGZM$LiLI z9cJ#O$twQsc^i*rU*J4_*Y@E){O$t=!(kqiJfJBju;r;u+^zTTcY1DOBD-crTfyD9 z?tY-Si;-To>ORRJ%o!8+2`>pSjiv9U^#Y{F=n}kw^N$e5uS<{=W5F)sLsi=cR#j8 z8~gTh@;QpqMD5Uc9kyPWF{GX7)I!nQZhKL%{WgWf_arq_`sbAQuQ6vd(69qF6@(%> z4wLCrRT4UiBVBpXnQ+;ta0Ybgq9tG^bHp?xwdJX7hLFUGa6y$7#*a!wZ8lrS+=#4mXPcTucG0hlwQYAiLRux(ya6o|!PUd)WE|<3yq?whMc+)V+y3e&y9>{< zlB@RbJb%+H8yK=gdh^$(PAxLhbpst^jOsyw3+s_N+Zzf#&y{!8^VjqxZXL8qQ4+Q7 zEqJ<{*9bLT1xDsTdw<%*C(CL~!K3w3jBg$^*TmxEvkfe$Q??jI*^Z@sZjd{|4C}Vl z-Y1ysInuWoYy)L(58HCab2o#_Gxm>dW>=?2h)y^QRH?Iq$gU z$vaU|u65W@O0yZ*uEpprOp@!wndk5KFiI;WSNcrYUQsX6)YzWP@kIqkkTZ_5$ElKx7EStH>&jOBZOf$9GB#d+W8m&;bz7=X6sTWbtmOHUO!ZNT zH`MgDi85X}WU}wMt_PqL((3<9}f#lHby8Qdx%kwE_(OG*b!DgLc^8L0C#LQR$8-c<$zjJE5UEUa6;c*pc{{n`}i7}*epK^I8&Q3q4iLwIo z?Xv0*u+#2ZPV;!$S5cLmIc|lb_%gRl5YyJR(!;U|@wnEph;3?x;tpe9a2_uF&YvIj z7B^t&O-}TwAnE&E@1qz;$`U)u*LA&j@yp##oIMcR6?d!-dtfDbLg@ly|IHo4D+Ag> z^lolweCk9rMxjq$R(BIXqE1}&lHU)=YNv8dQ?LwPP$dqVLIPEk8V^rSx5t0T$srOuuqoTUv6(U z-LYSvvgu~t`QX#Is-$-BQI{N#+jZDxNydN@W44%PIH586!~Mlr4$bcD+54wQB^sK1 zlB@WjOVO9q#E9@N z>A?(7#Zc%CPR7=EW1n%gE5F-Akcl-OKHPr2N~0gm~Yu?Dyk3iWi0BvKyQKG zSW{{JYRAI|gj)iw6A_{5*G}j^W?;MU0Yw<`YNOBW1E@m|6}+X-xKht2UUqMh2t(|uy`H!zGHU`_<;$kH2nntUCr*Kfe= zo(As!M6&7+EWLgucA}J#Z6nOQBvC)-ebnQ7A)Nxm9l5{6>_smNI+}#J0M_KKc^B86 z87N*qT$V1Ju9p$#vAOz0b~@h+qip`*Es0ca>X0^Ns5AU^MvqCn-J{V51+<;?&?)iv zw<()2)A&KNHG%J4IyCMPwFqg_JG1;(CGUxOkv{fMu5SY^Ep04fl^$Nc zg1oZ8Ey6w5R+Sk?9a01KN>_aJL~!%X!OW+}SJQY>KiE~IHXASNv#3BArvq6{|0o=e zEjk#ZpKueWf8wp6`&K{8B;Id4s10G5Z-wiQCaN}>L%{5N#*E9$ia!ce+JEBSNv!+l zyA6l=&zUZts>5EHhOHFXsLp?iNHmW{>4R|5H}I$XwYgC;J&gXZLwtu{_a1DLbY6w; zwY&D#{>r#KYHvkibvHAt!2?8Hs=np+?*V_6mnxifWRxl1PrD*kVQ`xA*jBYU430;- z8!IHb*epp>ej3Gt+N%vJ2FrkvJ{<=jzU+4`GdeQMp9ds$dX-di`lV#>DI22duq{f1 zU5wctTKB|IQ_6@zjaNli(2fzpbE};ye_cli0P+e(??N_UM zpE#Vo?%dPlw27YyT(=<+pjn@RU1p{{S*&mpx?P zH^~m-+1DeyZCzA(>#$suW=BdhXS^FT?$MsYhu5sG#8hGwc1JcWwSU>2o`|H?VUJaQ zWz3wU%?Rt8p3L#I2+Lj9B-i2jAj#HP8(6%13@55 zANp+-_kQutydD(A>ayd+wR8QM>H*BHt-;wP?UQ=~#y%wK^DFP;e-Yh#BXfR>hU1wx zDYsY<@XHg4^ydPB8Cd#L;pTuZ{hoM=&W;8dH{}ms8@YJ)p*?C;>3dLn@)&(*5cG-x zPW`!o$qlBdQa7n36m;#biG+j8#B+1DQNYHg&0ZYfu<-_abo%8RDvHJ=XUb01>MfF= zuXvHK)cqMszOKEHr?4&B{LKWx`iIM>e2ROi%y7%bG0KoURZoH9cDy=da97*Qvd8^D z#u96`*6r*AgZ&|y1zlQc&i__?^n0!CCmRjb$hwWQ;S-F&CC74YGYZ~37H2`Xqzb~= zbK_q}vO#bNwgzvUA|>0%c-qTGsxn=vLj%||I|C=a4mRH&QoE^xX)8JQ`bves_$7ud zSG>0cE+X`;&*6*Hi0!I#NH7V+7$-Z|4FL%~Yb4Wcs!u%KgjWtQIu+Y%Ni9-0?HNrq zNG&Pu=mnwA4>sCven3dC{!O%(9j>fG#mEkR?aw=HX1uq`EN$;dPJJ8^ZJP9QWlr$x zbJe#!>P$VrHi%`~1sLkS-EnW`WWGvtw(1qRF7E?ph$R$t3fL1gD5}ASR&JSKNVvG1 ih{1{F)P}A28s969$2r;>FBv2V(w@b|BPew*!fo zmVwYg&d5fGPKtvZ!5dg)^38q0{R9c~S`c940wK7cAyHlqB5s2DxUZpYkT5?#zW~3m zfPk=skf4x+jF_;nn2em1l#G;=oP;nhO}!>Fe|;iEgoH#yMZ`r##id0>MWx|~sPtqJ ziT^|Z_ckQK59vXAD1)5Zt$*ktpsx1RiL~hu|fkFq}{j0*U6sVEF|Eg+S;3 zOhiCP)HG2XLZA^SBpS(w#qjf?gbe{v0)<{6sn2KS9w?<8z1aXGotk^WT18dH<4%yF z5pBye8?4$v&;Hf{0^L~l4e4b8S_+Fpf8CA z8bm8g@fmDRm4-FAdv)Ke#YqkC z$a&WtjlUdCsD56;h13rx{n4#@yVq~qXu=2=>S*4{h1%DReBnY``2IxZM7J{+lK)Z? z!Wo*(I=Nm2k3^>d+tP(oj|v*G;j z*cO)ui^e5X4?4LXWTq(f9MjJpy_%?c&3*9@`G8$XQ{!AneCnh^ZF#fD6gs z-5;cG8R*c3B^77pF&>GgZTG>to+h-_^A}$^7__QuQy8u>jHnm}$iglia-%amgQ(O9 z9)}Z!K_XFNB4Gjy!*81i8k`s7mGvaY!yMLR23!UvPs$|GRs}|qyzKnE0BW+b$pUUP zDmB=X#E6M<4GJRwqA=tJ(I6`L3x;rzCq#l6Pz)3Wi-H8BX1GC)C&oI0L7CD>I4Fi3 z!5~w3A^}~1$8F=X+Ii9%OJNa6ZSeTfV#!%8c8y|?sT4Y(&w)8@q8OCvMu0{J(PlCp zA#|6Sj17%q^CLrH{K%{filEO>R)sKPXEJt?5w$b}UfvXjy^2Q!+_y8yI)aG%9o~Z;!QkQD z;v+0+x2O39V@U)Cm1Y~r2!va}Q_?NOjXpgpU>N2FZ&+<;B`J7P%wMYY0xS9Ns`Vs9 zOyb>W1k2U11YQy+(5O+qp(N0`V1$w>A=Bm)g$W)&Cu=H$L5+-{QbMMR2v1XB6=vZ@ zrtu!+kkHu#p=kopZH6D7E2vMCIv^HhT@WVksl$Z$E zkHK44Ob|7OLZts?ei9h^fChIn1P|(&kpuF78F`TPj}Z#10(txxfN&l|k029Bbf1Wo z@DxJ)R1Y7I2GEk=hvj9QY-iPl4WUtEqJBbQs5Ej2dB!{1dBW0Nc{m_LBrt|Sbs$kl zv_J-l2oR9hhNx-hE;z{yV_=F?WC#wt{;%7QilIgPv_$ZX|1-vg9`f@tf(eXZcm;<1 z#3f1~MMQX!;u%hKM=zI^(~gRN8vPTqKq!@#U==|QnYNVJWaAyD(V*p6A}Kg9hUYj1 zVo5Z{Z`ggN(Lb{b1%=p9BdD}l#+8_qZ|yKc07alxR0?cs0R}Y+EWmWqY|kVZ0bJNG zSm7X^%lic{1iCo%uk5^8w-Bxftgv7>hzOSaAP^kjK)4VJ zV0a5a=Rr|`L-OBn;2<0D2fH_({K%+xIn9GPgL>}%|z=MrT@3MD%x&`6#MtGoya z;RG|HGJZL3S-g3CWK<-H5r~VAjG)lX<1LhV!p#8;qji;WJQhZ{g);Ay2KW)77-$0yZTbcZBQRc@LRXoVU`wJCXk=bw zgo7o5U4IP2LK$c}*@Vcb8L_{$)ifiKFvAxGmU&*~M1n3Uf)q)jFzCRp`f&57g}S)R zu>V$UWaJDtonaRXH2S*^qrFJgww>v_0Pl(`L! z6bNDC&sn;H;E+9vu&dfH|tO8^wCXKJQLGSV|PGng)KOC`j>9XMT{ z2+Hdbhy*i2pnHY)tgcOiZ*5boAkI4jK+j;Yg-~PTepyb394W zQ&0pM#~Whifdu$`z(Sb-cRz`!JcTCz#)SWw2`0tHfvEOx2gW2Dof^!D3#5^(LV&sd z&9Kw`SE|zkWB*?9|E%x-wcktF8ZzZ$6=p<6JP-lAcC{rKmt9=#nT@Ck_CaS zBo5wF!thiA5$f0Hlc&lJ+Kz5G+%(}od`l3bXy6MC!VT~n5<&O{VE8C5B90LSA>~P#5y?J$y00`JgN!c zR62%+ql1GC5>ETCgZO`4aF$xL^jPdc3MSD=Akf75fKdj%-+}I?5Xo>rg(FjbipXY3 z_+J*AB>@H}$299>^vKHx+Nf_#;_CdWO1 z0iE~!>48iEStOmz`!WLAczEFmF|^o87Bu|g10V6kAZchWG#^rd7DC#P0ki~K3RyyS z&mdR-vx)>CyyKumXfw0}+5;Vc4nbK^4s;qSfQq4uP#JUsx&z&ZYQfd0 zCg?Tv9_obppnm8p^c`Hw!6HNuQi!<-C4?G63t@mTMJz|yAyy(h5UUY?AVLrnL<}Mk zu@$igaS)M($VC(&E+EPgcMuN{&k!#Wt%x4PAYvSeLJA|Lk%~xlq%P7FX@zt~dLh># zLy*zP1mrg4e&k`~N#r@?733XcEwTyu9@&Q+0k@`vP_n26C~cG}$_C|%@<#=uXsBcq z3zdO7i7G~2Lsg?1Q14M6QDbNfS_+LrYonK<9ns!s0y-L<;V^>^W=&wjSGw9px9|SK>F|x99igr|@s# z&)_fQzr|nA-^Kr3KwLmgz+AvpfGDs*AVuJ`z%_w7flh(%f)aunf);{af?gg26LJ?K3vCiQEcB;PjZmx5n6RX>EI}+o?2=fW z*hg^zadmMU@j&rp@oe#O@n-Q62`LGE30H|oiM-JQd^}?OI1mAN@Juoq@AS0r1walmwqDMFC!^qB;z9!FOw~EQ|7%aN>*Lg zNj5?@MfRfXbJ_7ZigT>ykml^3Q#_}B&NsPva+Y!=IhI_BT$9}RT-;o{x#4qD=a$WV zE031fly{eplRqwBCI4}r^t`3>2=iF;F3x+UfK<>_@KD&Oa7y8k!ieI0MF+(w#UqM$ z6+h0Goo_imY<~LuTl0IAq?DE`k(JVwDwKM0GB_(-1nw~I9>-_6;qX9m2{O#m3~zvRTtGn)gskbY65DeYN2YGYSn7r)HT$7)px3w zsdsD0X*g*lXcTF@Stzm)zc6~?$%T!JFpEqVg)cg`=&2@B(^xZ9Gh4Gx3#nzIMba5h+qEn{xSyxrpUpG~^T9>0|tQVnoTJN>K zr2Y#1E&A8=2Msh02nN{(jfNtI_J&NutA+zcT1F(J<3=xxrHoyScN$k3Pk^&Cnn|%q z&l1%oe=Iq=q}f#3)Xj9SX^k15nY9_y?1tHxxrsT${G$1nrP@owmliJVUZ$~(wCwb< zb_->TK#N?9_sen1|5$!v`Fs2Vd?5ZLzRgm_l4zM{*=4oJD$MGf)n{uxYnpYb^*0-H znn?Rptu0I=QB~zID@Zi*~#2j&XN$ z&vyUdq3@CCQSB+^8Q@vy`PIwXE5+-z_d@R&?@AvrpS3;(KHq$8eGmG+_tW)D@~d4f zznZ-Isz27>$3M@1WR2aLLu)$M8n4~4_W3%Eb@A&Ste0OOvHoU&SU_OFr9b%o@cpCk z4|bqy;K{&|Ag7?CLHz_9LMEY)Xh}>b_K++{2T5JQ7QqLDyF)BO(n5Md@u3-^AIUc4 zEb>5@V^~huw{X|+{BUlBZ$wEXf8-yL~bHnZpT?zIHXEvfY5;j&Q zE=b&**qUUOl$(r54oa?MsxY@PJ2yFOI=fkLbNJ@RTlBUZ+%mk?ck8unirY4AYv1m$ zy=aH%4%&|9olAG->_YD%?|QP^aQBhj92SxFaF5=ej6LkVguM^<>F+zdkGns3|C1Dx zl;a052Pg-gr{Yu39+WtkaIihiCGAQ&E}fM=d??`1gAAjLoJ_&Yn9TQwS0284MD@sl zBkZiutfp-1?2@AjM|U6nc8qwe{}V(sGG64{bH ze?ou8{Mmcqj|8p zu5&&A#)2E!H)U?7+!DOC?H0Eppvy;Dp8LHgRnAp4_wDcB zt+uMJsIjQI@xc5+`9sr(Wwj=?R~{KZDt&DHxb%t1lPh&g>aIRDdwT8JvS+vI@%48a zY#Z)3IyKfdt!jGq-23^9=C#f5Ul3n(zl?Y}@GADz_t%@=@V#NZ6@Q!YPT}3jmPIWk z?@iv{Y_)BD)aKdtx}DJ8*Fo=KcW(P2@*$&3sjHydp!-ITeb3Y0HNBmEQGMS(?)W6} z>FDQ$pD%y0{PL*ZufKDEHozU+J2ZDFf7ocaa>Q-q-Pg#k-@ma&=Z>BoGaaiQ_Z{#0 z9>>PA4^J$bxW;kjyx~TIOEW(%`dWg$z~n_=V2!#eua4{7PCq4`xFJUlPEI$^D z5fBg*1Q&vTP6!kV#fRn-z+ePK1O)^|#Ndm*V&J+kNK-H2$=uK7W^!EKMc*2z2894O zV}D%qoeM7df^99h(+wv8@DiF2g~TABsf)fq*V$C?>37yeUnEcsd=AH8QQ(23BakQw zG{gsP`dYbTqyoWBUulEXT&&FGMcYJ z?$)>Qt8Wz$l$*OP-Om2>WuFG`h4O>#L-1u^Qq1;4lNWyf#eH8>N59bcU5C$Jt$EQs zy2#9FHF?ACBL(FTUiOS>ny>H=OJHRcUVHeecYN|8CIMtg`*AOUkqFDF3AM->CfaUE=R!tm`tdnjgJi6kYHQ zDy~}hZ?YBKr~iGCXa9YX`k5ko!`IUX%Q7?UpU1q`#@BlcE^bghaO$Q&vF2FGYA$rN z9sKA|znp3Kk~kKozWJe}S0i$1V^Cp&E4xdH-G2X~&hffBli;O6h>)loMejxQy==MA zP`1(m+0G3z?P~(N|VvUo)siA5Y&qXmFrKS<5xSQ^p%Vl$|rQXV01HbNCM&)|G>1`+T!6 zmtu0>YG#g;S?6;&bS5X7A{9DkXrukrjZgEWw;!t!Ci{+tUhcJVYde>{5>$Et{9>Lf z*yVE~LJc31{Cso+_FTGhTl0Xx*k12X{`jnA*Gmc_=d8KZ*z+~x9WhqP7;~zp%5!pr zaT})kb*z~~6C1j|l6`WpbmC)u;k$9|?kx%Ok*qBo&k8&j>QZA*bXAy#`=*YuJ}+If zTMg5{MR7N z_j1nFq8<5X7oEM4RneEz$10xCDiGvCd5KeK>v(8UwoMqM$ zTlz!B+lrWuEj|@%GHxwkS6h(-x=)K+( zyUER|VJ5P^{eOm*EO0s%_;jb+-I2DlZ@|RmHclCp9l(Wha$DB>g|6sXH^aSRvo{!TpE&dB#*Rhj&#YEH5aFHPD8Z^IsT=#2^C9E> zSmv0}DW~&m-?a3~FO_c_4ESdNv65!+m~uIh%UX0MX4cjI!oqO*zmzHXyI#ku{aMOVuJ(WaGk zRcrpHwqi{OH+#+Jz(rlSfNhasdMOR{Y{+2NY|L1*pSZ5xnYP3pHQH9vbOTo4IZuJ; z?r7gOSN4Pl_U7PC8+l7jj3$TjI+%K-%BQU;9r;kqI<6|A| zCrhz)l+i*5CKpQf#k?cSL@dzt zhM7j#ui^Dq3+;e*b@|LLCEX0&XQ^+=bxN<6jqP}S)wEE>3h3aKY)y9CcAXVzRWo{g zRado4q6!znOM(`uu`jU?urK~Ol$jdIITMw+oA1r4mPo${hqtT>(G<8lFO;J|r?^)P z^_YH%WUA}tmZ$8cy-oPD)O?G6X>u7@QUbWmyj}?%#xM3BJq09N1@C;Asq=8f0lO%z zvCBzsQ_3rgqP=UsW`5B39kBt%ENZmt>Ug@_di&;0FQ@^D-s6{kU6zuKK%3sHHHvlG zrwHrJukB+si^si?*Bc8e`4Bo%*w1n9Cd`_lmMXhfHmi%UvPS(ck;h{1<&$&dKxu6~3V5YNkbFTd#4U1a&Ut*LF71 z`$A*lfbR(Cq~*ub@g2hhMcCof3cD<{SQY-iuHq$CIb&?COUeG-?if3F#@zClrLk23 zk5_>imB@UjF@9Hu>BAiAyPCo-*t5Ev<^v_YC>oguHU(UWJG~6Ew*qbQ*GP1zmKPwr znq`=JX<3gK3=miaLB)#EzQYqhl^xEbTu8%sGhNyE(%Y?Jxt7saMl7m#Xig;mx~r&= z#dq~PpU$aGn$tYObQH=RNyhc(CmcWgsv--xh{BGS>ED6VBY`e1IhX5NBcd}Xbm;2p zeR3rqQyUe$#{rPTR z7slewSxW8NU43t9Tgl6#z~la9xb+h_%z1eidpJH7LT>CsSvl1*ObakiydPX&_zevE zUQk&mS-dA_XdlNv-n#zdT)+LNkC#>joRkBirrdX|Ci^Ae%k4=EQySXuUsU;Q$CV>Q zQjD;lnBZ_#zyso@qCYLbiuwr5gP@WgCMQXZI>M}dOGw}4?<>-?|LU3VW-P_OdW;Ie zimrYR;gX9~ZI;01eXpgIA35B0J+2n;Ow~AOcPI8NJ)lCy zy=sl3y(TA>+4a2p5{JH+%9lHB=&=o ze-frj()7jEpcQ{VLPL{7d8Gaz8-%SO3Rc$LeZ3dc{xXXM{*kV)#} z&5G#_IvY0j!6XL9zSk*iZ||L({-TY*dVFu~9*syZdj&RSZll2H!+~J#GOlG%7mww} zm+xQVt^FP1LTd#l9##IbSqP}>Vsnld>z+$qgYSMw4mR7_<1aYADdp|f=GKm zfpvS^{1>@wW|;T*#M^}N^8ppoC%`BxdFDEHPi*W-c+1#*3n90s=P#ohB{}b1)b2#~ zyz!Jtx&(YUKBZBv{PN(%6I%TR7NVLNuYpP1O7C3>#Aix$X_uNKSb5;T1MAvDgp&dm zC#o$~dX=Yn6vPm9?dK+p=Op_^v0at(D>&#@O3c(|T`t zFHRrTj4RemRT^Vcifit&b#nJaSWO)DNn2gfHBi4f12ns(o#YdG|F+iOi+NNuE~Xc0kL$7Q*LG(Ke_7<;8jH}`;h zeF!?q^vvR4UCpdVZ(Ate3 zu3F8P(vkUL9T!rvd6>18PSN_F`DWrp(QpbU>Jlev-Vo~1yVwRW^v%2J-LKS-_C(dM zz3-bT$q&f`M_yu6uWh8Le%JBhUc1OZK^w`1Cr+}03a$X>HxpTW^nRQ0g>`~*`SJ3R z73*H!joMjQv$?{r%FENPUd2kDe5GdV$zADUO{_D%9H+tKc`1rtHdSGhSRJ{y?R_- zn0cBPl^(VzS5~FfUJ2aaG44E}?;SlB-jQ0%iRsf#`IOuPST-{s>efC`m8>}QadowI zU_cM+)SSVJ8m($h%sSIU-7ERQQeF`GRzsgg35TAZ(&oftD zzjFaKxE0CgysK+C?s}!=gCm=_a~hMkxN+nPfoIT8+||=lcIfhF?APStoCX=^oI&Q` zD;v*_2_q#5q*yB}?@B=Ca>s@y$>v(~M5`tKmd8M^XWogVMr>Ic?;))yA-(YX4$bqt cWfAP`CL^W)>f~p7<(mxU!7Tsxhea>{2di0&(f|Me literal 0 HcmV?d00001 diff --git a/Framework/Source/Data/HostDeviceData.h b/Source/Falcor/Data/HostDeviceData.h similarity index 96% rename from Framework/Source/Data/HostDeviceData.h rename to Source/Falcor/Data/HostDeviceData.h index 374386680..bb7542513 100644 --- a/Framework/Source/Data/HostDeviceData.h +++ b/Source/Falcor/Data/HostDeviceData.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/HostDeviceData.slang b/Source/Falcor/Data/HostDeviceData.slang similarity index 96% rename from Framework/Source/Data/HostDeviceData.slang rename to Source/Falcor/Data/HostDeviceData.slang index 100300b75..e41bbc209 100644 --- a/Framework/Source/Data/HostDeviceData.slang +++ b/Source/Falcor/Data/HostDeviceData.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Data/HostDeviceSharedCode.h b/Source/Falcor/Data/HostDeviceSharedCode.h similarity index 73% rename from Framework/Source/Data/HostDeviceSharedCode.h rename to Source/Falcor/Data/HostDeviceSharedCode.h index f733f4449..27384d0b9 100644 --- a/Framework/Source/Data/HostDeviceSharedCode.h +++ b/Source/Falcor/Data/HostDeviceSharedCode.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -34,15 +34,15 @@ #include "glm/gtx/compatibility.hpp" -using glm::float2; -using glm::float3; -using glm::float4; - -using glm::float2x2; -using glm::float3x3; -using glm::float4x4; +namespace Falcor +{ + using uint2 = glm::uvec2; + using uint3 = glm::uvec3; + using uint4 = glm::uvec4; -namespace Falcor { + using int2 = glm::ivec2; + using int3 = glm::ivec3; + using int4 = glm::ivec4; /******************************************************************* CPU declarations *******************************************************************/ @@ -61,36 +61,96 @@ typedef int int32_t; /******************************************************************* -Camera + Scene Geometry +*******************************************************************/ +struct MeshDesc +{ + uint vbOffset; + uint ibOffset; + uint vertexCount; // #SCENE This is probably only needed on the CPU + uint indexCount; // #SCENE This is probably only needed on the CPU + uint materialID; +}; + +enum MeshInstanceFlags +{ + None = 0x0, + Flipped = 0x1 +}; + +struct MeshInstanceData +{ + uint globalMatrixID; + uint materialID; + uint meshID; + uint flags; ///< MeshInstanceFlags +}; + +struct StaticVertexData +{ + float3 position; + float3 normal; + float3 bitangent; + float2 texCrd; + float3 prevPosition; +}; + +struct DynamicVertexData +{ + uint4 boneID; + float4 boneWeight; + uint staticIndex; // The index in the static vertex buffer + uint globalMatrixID; +}; + +//struct OptionalVertexData +//{ +// float3 lightmapUV; +//}; + +struct VertexData +{ + float3 posW; ///< Position in world space. + float3 normalW; ///< Shading normal in world space. + float3 bitangentW; ///< Shading bitangent in world space. + float2 texC; ///< Texture coordinate. + float3 faceNormalW; ///< Face normal in world space. +}; + +/******************************************************************* + Camera *******************************************************************/ /** This is a general host/device structure that describe a camera. */ struct CameraData { - float4x4 viewMat; ///< Camera view matrix. - float4x4 projMat; ///< Camera projection matrix. - float4x4 viewProjMat; ///< Camera view-projection matrix. - float4x4 invViewProj; ///< Camera inverse view-projection matrix. - float4x4 prevViewProjMat; ///< Camera view-projection matrix associated to previous frame. No jittering is applied! + float4x4 viewMat; ///< Camera view matrix. + float4x4 projMat; ///< Camera projection matrix. + float4x4 viewProjMat; ///< Camera view-projection matrix. + float4x4 invViewProj; ///< Camera inverse view-projection matrix. + float4x4 viewProjMatNoJitter; ///< Camera view-projection matrix. No jittering is applied! + float4x4 prevViewProjMatNoJitter; ///< Camera view-projection matrix associated to previous frame. No jittering is applied! float3 posW; ///< Camera world-space position. float focalLength DEFAULTS(21.0f); ///< Camera focal length in mm. Default is 59 degree vertical, 90 horizontal FOV at 16:9 aspect ratio. float3 up DEFAULTS(float3(0, 1, 0)); ///< Camera world-space up vector. - float aspectRatio DEFAULTS(1.7777f); ///< 16:9 aspect-ratio + float aspectRatio DEFAULTS(1.7777f); ///< 16:9 aspect-ratio. float3 target DEFAULTS(float3(0, 0, -1)); ///< Camera target point in world-space. float nearZ DEFAULTS(0.1f); ///< Camera near plane. float3 cameraU DEFAULTS(float3(0, 0, 1)); ///< Camera base vector U. Normalized it indicates the right image plane vector. The length is dependent on the FOV. float farZ DEFAULTS(1000.0f); ///< Camera far plane. float3 cameraV DEFAULTS(float3(0, 1, 0)); ///< Camera base vector V. Normalized it indicates the up image plane vector. The length is dependent on the FOV. - float jitterX DEFAULTS(0.0f); ///< Eventual camera jitter in the x coordinate + float jitterX DEFAULTS(0.0f); ///< Eventual camera jitter along the x axis expressed as a subpixel offset divided by screen width (positive value shifts the image right). float3 cameraW DEFAULTS(float3(1, 0, 0)); ///< Camera base vector W. Normalized it indicates the forward direction. The length is the camera focal distance. - float jitterY DEFAULTS(0.0f); ///< Eventual camera jitter in the y coordinate + float jitterY DEFAULTS(0.0f); ///< Eventual camera jitter along the y axis expressed as a subpixel offset divided by screen height (positive value shifts the image up). - float frameHeight DEFAULTS(24.0f); ///< Camera film plane height in mm. + float frameHeight DEFAULTS(24.0f); ///< Camera film plane height in mm. 24 is the height of a 35mm film float focalDistance DEFAULTS(10000.0f); ///< Camera focal distance in scene units. - float apertureRadius DEFAULTS(0.0f); ///< Camera aperture radius in scene units. - float _padding; + float apertureRadius DEFAULTS(0.0f); ///< Camera aperture radius in scene units. + float shutterSpeed DEFAULTS(0.004f); ///< Camera shutter speed in seconds. + float ISOSpeed DEFAULTS(100.0f); ///< Camera film speed based on ISO standards. + float3 _padding; float4x4 rightEyeViewMat; float4x4 rightEyeProjMat; @@ -118,18 +178,18 @@ struct MaterialResources struct MaterialData { - float4 baseColor DEFAULTS(float4(1)); - float4 specular DEFAULTS(float4(0)); - float3 emissive DEFAULTS(float3(0)); - float padf DEFAULTS(0); + float4 baseColor DEFAULTS(float4(1)); ///< Material base color (RGB) and opacity (A). + float4 specular DEFAULTS(float4(0)); ///< Material specular channel encoding occlusion (R), roughness (G), metallness (B) in the default SpecRough mode. + float3 emissive DEFAULTS(float3(0, 0, 0)); ///< Emissive color (RGB). + float emissiveFactor DEFAULTS(1.f); ///< Multiplication factor for the emissive color to control light intensity. - float alphaThreshold DEFAULTS(0.5f); // Used in case the alpha mode is mask - float IoR DEFAULTS(1); // Index of refraction - uint32_t id; - uint32_t flags DEFAULTS(0); + float alphaThreshold DEFAULTS(0.5f); ///< Alpha threshold, only used in case the alpha mode is mask. + float IoR DEFAULTS(1.f); ///< Index of refraction. + uint32_t flags DEFAULTS(0); + uint pad0 DEFAULTS(0); - float2 heightScaleOffset DEFAULTS(float2(1, 0)); - float2 pad DEFAULTS(float2(0)); + float2 heightScaleOffset DEFAULTS(float2(1, 0)); + float2 pad1 DEFAULTS(float2(0)); MaterialResources resources; }; @@ -171,27 +231,25 @@ struct AreaLightResources RAW_BUFFER vertexBuffer; ///< Buffer for vertices (float3) RAW_BUFFER texCoordBuffer; ///< Buffer for vertices (float2) RAW_BUFFER meshCDFBuffer; ///< Buffer for vertices (float) - - MaterialData material; ///< Emissive material of the geometry mesh }; struct AreaLightData { float3 posW DEFAULTS(float3()); ///< World-space position the light source float surfaceArea DEFAULTS(0.f); ///< Surface area of the geometry mesh - float3 dirW DEFAULTS(float3()); ///< World-space orientation of the light source + float3 dirW DEFAULTS(float3()); ///< World-space orientation of the light source (normalized). uint32_t numTriangles DEFAULTS(0); ///< Number of triangles in a polygonal area light float3 intensity DEFAULTS(float3(1.0f)); ///< Emitted radiance of the light source - float pad0; + uint32_t materialId DEFAULTS(0); ///< Material ID of the geometry mesh in the scene float3 tangent DEFAULTS(float3()); ///< Tangent vector of the geometry mesh - float pad1; + float pad0; float3 bitangent DEFAULTS(float3()); ///< Bitangent vector of the geometry mesh - float pad2; + float pad1; float3 aabbMin DEFAULTS(float3(1e20f)); ///< Minimum corner of the AABB - float pad3; + float pad2; float3 aabbMax DEFAULTS(float3(-1e20f)); ///< Maximum corner of the AABB - float pad4; - float4x4 transMat DEFAULTS(float4x4()); ///< Transformation matrix of the area light + float pad3; + float4x4 transMat DEFAULTS(float4x4()); ///< Transformation matrix of the area light, from local to world space. TODO: Does not include model instance transform yet, should be fixed. AreaLightResources resources; }; @@ -200,20 +258,20 @@ struct LightData { float3 posW DEFAULTS(float3(0, 0, 0)); ///< World-space position of the center of a light source uint32_t type DEFAULTS(LightPoint); ///< Type of the light source (see above) - float3 dirW DEFAULTS(float3(0, -1, 0)); ///< World-space orientation of the light source - float openingAngle DEFAULTS(3.14159265f); ///< For point (spot) light: Opening angle of a spot light cut-off, pi by default - full-sphere point light + float3 dirW DEFAULTS(float3(0, -1, 0)); ///< World-space orientation of the light source (normalized). + float openingAngle DEFAULTS(float(M_PI2)); ///< For point (spot) light: Opening angle of a spot light cut-off, pi by default - full-sphere point light float3 intensity DEFAULTS(float3(1, 1, 1)); ///< Emitted radiance of th light source float cosOpeningAngle DEFAULTS(-1.f); ///< For point (spot) light: cos(openingAngle), -1 by default because openingAngle is pi by default float3 pad; float penumbraAngle DEFAULTS(0.f); ///< For point (spot) light: Opening angle of penumbra region in radians, usually does not exceed openingAngle. 0.f by default, meaning a spot light with hard cut-off // Extra parameters for analytic area lights - float3 tangent DEFAULTS(float3()); ///< Tangent vector of the light shape - float surfaceArea DEFAULTS(0.f); ///< Surface area of the light shape - float3 bitangent DEFAULTS(float3()); ///< Bitangent vector of the light shape + float3 tangent DEFAULTS(float3()); ///< Tangent vector of the light shape + float surfaceArea DEFAULTS(0.f); ///< Surface area of the light shape + float3 bitangent DEFAULTS(float3()); ///< Bitangent vector of the light shape float pad1; - float4x4 transMat DEFAULTS(float4x4()); ///< Transformation matrix of the light shape - float4x4 transMatIT DEFAULTS(float4x4()); ///< Inverse-transpose of transformation matrix of the light shape + float4x4 transMat DEFAULTS(float4x4()); ///< Transformation matrix of the light shape, from local to world space. + float4x4 transMatIT DEFAULTS(float4x4()); ///< Inverse-transpose of transformation matrix of the light shape }; /******************************************************************* diff --git a/Framework/Source/Data/HostDeviceSharedMacros.h b/Source/Falcor/Data/HostDeviceSharedMacros.h similarity index 96% rename from Framework/Source/Data/HostDeviceSharedMacros.h rename to Source/Falcor/Data/HostDeviceSharedMacros.h index e8592449f..9bf6796f0 100644 --- a/Framework/Source/Data/HostDeviceSharedMacros.h +++ b/Source/Falcor/Data/HostDeviceSharedMacros.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,7 +33,6 @@ *******************************************************************/ #define MAX_INSTANCES 64 ///< Max supported instances per draw call -#define MAX_BONES 256 ///< Max supported bones per model /******************************************************************* Glue code for CPU/GPU compilation @@ -47,6 +46,8 @@ #ifdef HOST_CODE +#define BEGIN_NAMESPACE_FALCOR namespace Falcor{ +#define END_NAMESPACE_FALCOR } /******************************************************************* CPU declarations @@ -54,12 +55,18 @@ #define DEFAULTS(x_) = x_ #define SamplerState std::shared_ptr #define Texture2D std::shared_ptr +#define SETTER_DECL +#define CONST_FUNCTION const #else /******************************************************************* HLSL declarations *******************************************************************/ #define inline #define DEFAULTS(x_) +#define SETTER_DECL [mutating] +#define BEGIN_NAMESPACE_FALCOR +#define END_NAMESPACE_FALCOR +#define CONST_FUNCTION #endif /******************************************************************* diff --git a/Framework/Source/Data/DefaultVS.slang b/Source/Falcor/Data/Raster.slang similarity index 53% rename from Framework/Source/Data/DefaultVS.slang rename to Source/Falcor/Data/Raster.slang index abf07c93f..107808dc5 100644 --- a/Framework/Source/Data/DefaultVS.slang +++ b/Source/Falcor/Data/Raster.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,41 +26,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include "VertexAttrib.h" -__import ShaderCommon; +import Shading; +import Scene; -struct VertexIn +struct VSIn { - float4 pos : POSITION; -#ifdef HAS_NORMAL - float3 normal : NORMAL; -#endif -#ifdef HAS_BITANGENT - float3 bitangent : BITANGENT; -#endif -#ifdef HAS_TEXCRD - float2 texC : TEXCOORD; -#endif -#ifdef HAS_LIGHTMAP_UV - float2 lightmapC : LIGHTMAP_UV; -#endif -#ifdef HAS_COLORS - float3 color : DIFFUSE_COLOR; -#endif -#ifdef _VERTEX_BLENDING - float4 boneWeights : BONE_WEIGHTS; - uint4 boneIds : BONE_IDS; -#endif -#ifdef HAS_PREV_POSITION - float4 prevPos : PREV_POSITION; -#endif - uint instanceID : SV_INSTANCEID; + float4 pos : POSITION; + float3 normal : NORMAL; + float3 bitangent : BITANGENT; + float2 texC : TEXCOORD; + uint meshInstanceID : DRAW_ID; + float4 prevPos : PREV_POSITION; +//#ifdef HAS_LIGHTMAP_UV +// float2 lightmapC : LIGHTMAP_UV; +//#endif }; #ifndef INTERPOLATION_MODE #define INTERPOLATION_MODE linear #endif -struct VertexOut +struct VSOut { INTERPOLATION_MODE float3 normalW : NORMAL; INTERPOLATION_MODE float3 bitangentW : BITANGENT; @@ -68,8 +54,13 @@ struct VertexOut INTERPOLATION_MODE float3 posW : POSW; INTERPOLATION_MODE float3 colorV : COLOR; INTERPOLATION_MODE float4 prevPosH : PREVPOSH; - INTERPOLATION_MODE float2 lightmapC : LIGHTMAPUV; + + // Per-triangle data + nointerpolation uint meshInstanceID : DRAW_ID; + nointerpolation uint materialID : MATERIAL_ID; + float4 posH : SV_POSITION; + #ifdef _SINGLE_PASS_STEREO INTERPOLATION_MODE float4 rightEyePosS : NV_X_RIGHT; uint4 viewportMask : NV_VIEWPORT_MASK; @@ -77,79 +68,50 @@ struct VertexOut #endif }; -float4x4 getWorldMat(VertexIn vIn) +VSOut defaultVS(VSIn vIn) { - float4x4 worldMat = gWorldMat[vIn.instanceID]; - -#ifdef _VERTEX_BLENDING - worldMat = mul(getBlendedBoneMat(vIn.boneWeights, vIn.boneIds), worldMat); -#endif - - return worldMat; -} - -float3x3 getWorldInvTransposeMat(VertexIn vIn) -{ - float3x3 worldInvTransposeMat = (float3x3)gWorldInvTransposeMat[vIn.instanceID]; - -#ifdef _VERTEX_BLENDING - worldInvTransposeMat = mul(getBlendedInvTransposeBoneMat(vIn.boneWeights, vIn.boneIds), worldInvTransposeMat); -#endif - - return worldInvTransposeMat; -} - -VertexOut defaultVS(VertexIn vIn) -{ - VertexOut vOut; - float4x4 worldMat = getWorldMat(vIn); + VSOut vOut; + float4x4 worldMat = gScene.getWorldMatrix(vIn.meshInstanceID); float4 posW = mul(vIn.pos, worldMat); vOut.posW = posW.xyz; - vOut.posH = mul(posW, gCamera.viewProjMat); - -#ifdef HAS_TEXCRD - vOut.texC = vIn.texC; -#else - vOut.texC = 0; -#endif + vOut.posH = mul(posW, gScene.camera.viewProjMat); -#ifdef HAS_COLORS - vOut.colorV = vIn.color; -#else - vOut.colorV = 0; -#endif - -#ifdef HAS_NORMAL - vOut.normalW = mul(vIn.normal, getWorldInvTransposeMat(vIn)).xyz; -#else - vOut.normalW = 0; -#endif - -#ifdef HAS_BITANGENT - vOut.bitangentW = mul(vIn.bitangent, (float3x3)getWorldMat(vIn)); -#else - vOut.bitangentW = 0; -#endif + vOut.meshInstanceID = vIn.meshInstanceID; + vOut.materialID = gScene.getMaterialID(vIn.meshInstanceID); -#ifdef HAS_LIGHTMAP_UV - vOut.lightmapC = vIn.lightmapC; -#else - vOut.lightmapC = 0; -#endif + vOut.texC = vIn.texC; + vOut.normalW = mul(vIn.normal, (float3x3)gScene.getInverseTransposeWorldMatrix(vIn.meshInstanceID)).xyz; + vOut.bitangentW = mul(vIn.bitangent,(float3x3) gScene.getWorldMatrix(vIn.meshInstanceID)); -#ifdef HAS_PREV_POSITION - float4 prevPos = vIn.prevPos; -#else - float4 prevPos = vIn.pos; -#endif - float4 prevPosW = mul(prevPos, gPrevWorldMat[vIn.instanceID]); - vOut.prevPosH = mul(prevPosW, gCamera.prevViewProjMat); + float4 prevPosW = mul(vIn.prevPos, gScene.getPrevWorldMatrix(vIn.meshInstanceID)); + vOut.prevPosH = mul(prevPosW, gScene.camera.prevViewProjMatNoJitter); #ifdef _SINGLE_PASS_STEREO - vOut.rightEyePosS = mul(posW, gCamera.rightEyeViewProjMat).x; + vOut.rightEyePosS = mul(posW, gScene.camera.rightEyeViewProjMat).x; vOut.viewportMask = 0x00000001; vOut.renderTargetIndex = 0; #endif return vOut; } + +VertexData prepareVertexData(VSOut vsOut, float3 faceNormalW) +{ + VertexData v; + v.posW = vsOut.posW; + v.normalW = vsOut.normalW; + v.bitangentW = vsOut.bitangentW; + v.texC = vsOut.texC; + v.faceNormalW = faceNormalW; + return v; +} + +/** Helper function that prepares the ShadingData struct based on VSOut. + This version uses implicit LOD and fetches material data directly from the scene. +*/ +ShadingData prepareShadingData(VSOut vsOut, uint triangleIndex, float3 camPosW) +{ + float3 faceNormal = gScene.getFaceNormalW(vsOut.meshInstanceID, triangleIndex); + VertexData v = prepareVertexData(vsOut, faceNormal); + return prepareShadingData(v, gScene.materials[vsOut.materialID], camPosW); +} diff --git a/Samples/ForwardRenderer/Data/DepthPass.ps.slang b/Source/Falcor/Data/RenderPasses/DepthPass.ps.slang similarity index 85% rename from Samples/ForwardRenderer/Data/DepthPass.ps.slang rename to Source/Falcor/Data/RenderPasses/DepthPass.ps.slang index 7f530153f..a2378cd07 100644 --- a/Samples/ForwardRenderer/Data/DepthPass.ps.slang +++ b/Source/Falcor/Data/RenderPasses/DepthPass.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import DefaultVS; -__import ShaderCommon; -__import Shading; +import Raster; +import ShaderCommon; +import Shading; +import Scene; -void main(VertexOut vOut) +void main(VSOut vOut, uint triangleIndex : SV_PrimitiveID) : SV_TARGET { - prepareShadingData(vOut, gMaterial, gCamera.posW); -} \ No newline at end of file + prepareShadingData(vOut, triangleIndex, gScene.camera.posW); +} diff --git a/Framework/Source/Data/RenderPasses/ForwardLightingPass.slang b/Source/Falcor/Data/RenderPasses/ForwardLightingPass.slang similarity index 80% rename from Framework/Source/Data/RenderPasses/ForwardLightingPass.slang rename to Source/Falcor/Data/RenderPasses/ForwardLightingPass.slang index f3235bc6e..5083c61b4 100644 --- a/Framework/Source/Data/RenderPasses/ForwardLightingPass.slang +++ b/Source/Falcor/Data/RenderPasses/ForwardLightingPass.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,10 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ __import ShaderCommon; -__import DefaultVS; +__import Raster; __import Shading; __import Helpers; __import BRDF; +__import Scene; cbuffer PerFrameCB { @@ -38,13 +39,14 @@ cbuffer PerFrameCB SamplerState gSampler; Texture2D visibilityBuffer; +static VSOut vsData; -VertexOut vs(VertexIn vIn) +VSOut vs(VSIn vIn) { - VertexOut vsOut; + VSOut vsOut; vsOut = defaultVS(vIn); #ifdef _OUTPUT_MOTION_VECTORS - vsOut.prevPosH.xy += vsOut.prevPosH.w * 2 * float2(gCamera.jitterX, gCamera.jitterY); + vsOut.prevPosH.xy += vsOut.prevPosH.w * 2 * float2(gScene.camera.jitterX, gScene.camera.jitterY); #endif return vsOut; } @@ -58,15 +60,15 @@ struct PsOut #endif }; -PsOut ps(VertexOut vOut, float4 pixelCrd : SV_POSITION) +PsOut ps(VSOut vOut, float4 pixelCrd : SV_POSITION, uint triangleIndex : SV_PrimitiveID) { PsOut psOut; - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); + ShadingData sd = prepareShadingData(vOut, triangleIndex, gScene.camera.posW); float4 finalColor = float4(0, 0, 0, 1); - for (uint l = 0; l < gLightsCount; l++) + for (uint l = 0; l < gScene.getLightCount(); l++) { float shadowFactor = 1; if (l == 0) @@ -74,17 +76,17 @@ PsOut ps(VertexOut vOut, float4 pixelCrd : SV_POSITION) shadowFactor = visibilityBuffer.Load(int3(vOut.posH.xy, 0)).r; shadowFactor *= sd.opacity; } - finalColor.rgb += evalMaterial(sd, gLights[l], shadowFactor).color.rgb; + finalColor.rgb += evalMaterial(sd, gScene.getLight(l), shadowFactor).color.rgb; } // Add the emissive component finalColor.rgb += sd.emissive; finalColor.a = sd.opacity; - finalColor.rgb += evalMaterial(sd, gLightProbe).color.rgb; + finalColor.rgb += evalMaterial(sd, gScene.lightProbe).color.rgb; // Add light-map - finalColor.rgb += sd.diffuse * sd.lightMap.rgb; +// finalColor.rgb += sd.diffuse * sd.lightMap.rgb; psOut.color = finalColor; psOut.normal = float4(vOut.normalW * 0.5f + 0.5f, 1.0f); diff --git a/Framework/Source/Data/ShaderCommon.slang b/Source/Falcor/Data/ShaderCommon.slang similarity index 59% rename from Framework/Source/Data/ShaderCommon.slang rename to Source/Falcor/Data/ShaderCommon.slang index 7ae35b085..463faaefe 100644 --- a/Framework/Source/Data/ShaderCommon.slang +++ b/Source/Falcor/Data/ShaderCommon.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,60 +25,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#ifndef _FALCOR_SHADER_COMMON_H_ -#define _FALCOR_SHADER_COMMON_H_ - #include "HostDeviceData.h" +import Helpers; -shared cbuffer InternalPerFrameCB : register(b10) -{ - CameraData gCamera; - uint32_t gLightsCount; - float3 internalPerFrameCBPad; - LightData gLights[MAX_LIGHT_SOURCES]; - LightProbeData gLightProbe; - LightProbeSharedResources gProbeShared; -}; - -cbuffer InternalPerMeshCB -{ - float4x4 gWorldMat[MAX_INSTANCES]; // Per-instance world transforms - float4x4 gPrevWorldMat[MAX_INSTANCES]; // Previous frame world transforms - float3x4 gWorldInvTransposeMat[MAX_INSTANCES]; // Per-instance matrices for transforming normals - uint32_t gDrawId[MAX_INSTANCES]; // Zero-based order/ID of Mesh Instances drawn per SceneRenderer::renderScene call. - uint32_t gMeshId; -}; - -cbuffer InternalBoneCB -{ - float4x4 gBoneMat[MAX_BONES]; // Per-model bone matrices - float4x4 gInvTransposeBoneMat[MAX_BONES]; // Per-model bone inverse transpose matrices -}; - -#ifdef _VERTEX_BLENDING -float4x4 getBlendedBoneMat(float4 weights, uint4 ids) -{ - float4x4 boneMat = gBoneMat[ids.x] * weights.x; - boneMat += gBoneMat[ids.y] * weights.y; - boneMat += gBoneMat[ids.z] * weights.z; - boneMat += gBoneMat[ids.w] * weights.w; - - return boneMat; -} - -float3x3 getBlendedInvTransposeBoneMat(float4 weights, uint4 ids) -{ - float3x3 mat = (float3x3)gInvTransposeBoneMat[ids.x] * weights.x; - mat += (float3x3)gInvTransposeBoneMat[ids.y] * weights.y; - mat += (float3x3)gInvTransposeBoneMat[ids.z] * weights.z; - mat += (float3x3)gInvTransposeBoneMat[ids.w] * weights.w; - - return mat; -} -#endif - -ParameterBlock gMaterial; - +/** Calculate screen-space motion vector. + \param[in] pixelCrd Sample in current frame expressed in pixel coordinates with origin in the top-left corner. + \param[in] prevPosH Sample in previous frame expressed in homogeneous clip space coordinates. Note that the definition differs between D3D12 and Vulkan. + \param[in] renderTargetDim Render target dimension in pixels. + \return Motion vector pointing from current to previous position expressed in sceen space [0,1] with origin in the top-left corner. +*/ float2 calcMotionVector(float2 pixelCrd, float4 prevPosH, float2 renderTargetDim) { float2 prevCrd = prevPosH.xy / prevPosH.w; @@ -102,20 +57,37 @@ struct ShadingData float3 T; ///< Shading tangent at shading hit float3 B; ///< Shading bitangent at shading hit float2 uv; ///< Texture mapping coordinates - float NdotV; // Unclamped, can be negative + float NdotV; // Unclamped, can be negative. + + // Primitive data + float3 faceN; ///< Face normal in world space, always on the front-facing side. + bool frontFacing; ///< True if primitive seen from the front-facing side. + bool doubleSided; ///< Material double-sided flag, if false only shade front face. // Pre-loaded texture data float3 diffuse; float opacity; float3 specular; - float linearRoughness; // This is the original roughness, before re-mapping. It is required for the Disney diffuse term - float roughness; // This is the re-mapped roughness value, which should be used for GGX computations + float linearRoughness; // This is the original roughness, before re-mapping. It is required for the Disney diffuse term + float ggxAlpha; // This is the re-mapped roughness value, which should be used for GGX computations. It equals `roughness^2` float3 emissive; float4 occlusion; - float3 lightMap; float2 height; float IoR; - bool doubleSidedMaterial; + float metallic; // If the shading model is metal/rough, this is the metallic paramter. Otherwise, it will be 0 + + // Utility functions + + /** Computes new ray origin based on the hit point to avoid self-intersection. + The method is described in Ray Tracing Gems, Chapter 6, "A Fast and Robust + Method for Avoiding Self-Intersection" by Carsten Wächter and Nikolaus Binder. + \param[in] viewside True if the origin should be on the view side (reflection) or false otherwise (transmission). + \return Ray origin of the new ray. + */ + float3 computeNewRayOrigin(bool viewside = true) + { + return computeRayOrigin(posW, (frontFacing == viewside) ? faceN : -faceN); + } }; #define interfacify(a) typedef a I##a @@ -133,5 +105,3 @@ interfacify(RWTexture2D); interfacify(RWTexture2DArray); interfacify(RWTexture3D); #undef interfacify - -#endif // _FALCOR_SHADER_COMMON_H_ \ No newline at end of file diff --git a/Framework/Source/Data/UnitTest.cs.hlsl b/Source/Falcor/Data/UnitTest.cs.hlsl similarity index 100% rename from Framework/Source/Data/UnitTest.cs.hlsl rename to Source/Falcor/Data/UnitTest.cs.hlsl diff --git a/Framework/Source/Data/VertexAttrib.h b/Source/Falcor/Data/VertexAttrib.h similarity index 80% rename from Framework/Source/Data/VertexAttrib.h rename to Source/Falcor/Data/VertexAttrib.h index 7910a25e4..fa3577c54 100644 --- a/Framework/Source/Data/VertexAttrib.h +++ b/Source/Falcor/Data/VertexAttrib.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,31 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#pragma once + +enum class VertexElement +{ + Position, + Normal, + Bitangent, + TexCoord, + LightmapUV, + PrevPosition, + DrawID, + // Many places in code iterate based on VertexElement, or check element index directly, so instance data isn't part of this enum + + Count // Must be last +}; #define VERTEX_POSITION_LOC 0 #define VERTEX_NORMAL_LOC 1 #define VERTEX_BITANGENT_LOC 2 #define VERTEX_TEXCOORD_LOC 3 #define VERTEX_LIGHTMAP_UV_LOC 4 -#define VERTEX_BONE_WEIGHT_LOC 5 -#define VERTEX_BONE_ID_LOC 6 -#define VERTEX_DIFFUSE_COLOR_LOC 7 -#define VERTEX_PREV_POSITION_LOC 8 +#define VERTEX_PREV_POSITION_LOC 6 +#define INSTANCE_DRAW_ID_LOC 7 -#define VERTEX_LOCATION_COUNT 9 +#define VERTEX_LOCATION_COUNT 8 #define VERTEX_USER_ELEM_COUNT 4 #define VERTEX_USER0_LOC (VERTEX_LOCATION_COUNT) @@ -46,7 +59,5 @@ #define VERTEX_BITANGENT_NAME "BITANGENT" #define VERTEX_TEXCOORD_NAME "TEXCOORD" #define VERTEX_LIGHTMAP_UV_NAME "LIGHTMAP_UV" -#define VERTEX_BONE_WEIGHT_NAME "BONE_WEIGHTS" -#define VERTEX_BONE_ID_NAME "BONE_IDS" -#define VERTEX_DIFFUSE_COLOR_NAME "DIFFUSE_COLOR" #define VERTEX_PREV_POSITION_NAME "PREV_POSITION" +#define INSTANCE_DRAW_ID_NAME "DRAW_ID" diff --git a/Source/Falcor/Effects/AmbientOcclusion/SSAOPass.cpp b/Source/Falcor/Effects/AmbientOcclusion/SSAOPass.cpp new file mode 100644 index 000000000..556152758 --- /dev/null +++ b/Source/Falcor/Effects/AmbientOcclusion/SSAOPass.cpp @@ -0,0 +1,283 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "SSAOPass.h" +#include "glm/gtc/random.hpp" + +namespace Falcor +{ + const char* SSAOPass::kDesc = "Screen-space ambient occlusion. Can be used with and without a normal-map"; + + namespace + { + const Gui::DropdownList kDistributionDropdown = + { + { (uint32_t)SSAOPass::SampleDistribution::Random, "Random" }, + { (uint32_t)SSAOPass::SampleDistribution::UniformHammersley, "Uniform Hammersley" }, + { (uint32_t)SSAOPass::SampleDistribution::CosineHammersley, "Cosine Hammersley" } + }; + + const std::string kAoMapSize = "aoMapSize"; + const std::string kKernelSize = "kernelSize"; + const std::string kNoiseSize = "noiseSize"; + const std::string kDistribution = "distribution"; + const std::string kBlurDict = "blurDict"; + + const std::string kColorIn = "colorIn"; + const std::string kColorOut = "colorOut"; + const std::string kDepth = "depth"; + const std::string kNormals = "normals"; + const std::string kAoMap = "AoMap"; + + const std::string kSSAOShader = "Effects/SSAO.ps.slang"; + const std::string kApplySSAOShader = "ApplyAO.ps.slang"; + } + + SSAOPass::SSAOPass() + { + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap); + mpNoiseSampler = Sampler::create(samplerDesc); + + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + mpTextureSampler = Sampler::create(samplerDesc); + + mpSSAOPass = FullScreenPass::create(kSSAOShader); + mComposeData.pApplySSAOPass = FullScreenPass::create(kApplySSAOShader); + Sampler::Desc desc; + desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + mComposeData.pApplySSAOPass["gSampler"] = Sampler::create(desc); + mComposeData.pFbo = Fbo::create(); + } + + SSAOPass::SharedPtr SSAOPass::create(RenderContext* pRenderContext, const Dictionary& dict) + { + SharedPtr pSSAO = SharedPtr(new SSAOPass); + Dictionary blurDict; + for (const auto& v : dict) + { + if (v.key() == kAoMapSize) pSSAO->mAoMapSize = v.val(); + if (v.key() == kKernelSize) pSSAO->mData.kernelSize = v.val(); + if (v.key() == kNoiseSize) pSSAO->mNoiseSize = v.val(); + if (v.key() == kDistribution) pSSAO->mHemisphereDistribution = v.val(); + if (v.key() == kBlurDict) pSSAO->mBlurDict = v.val(); + else logWarning("Unknown field '" + v.key() + "' in a SSAOPass dictionary"); + } + return pSSAO; + } + + Dictionary SSAOPass::getScriptingDictionary() + { + Dictionary dict; + dict[kAoMapSize] = mAoMapSize; + dict[kKernelSize] = mData.kernelSize; + dict[kNoiseSize] = mNoiseSize; + dict[kDistribution] = mHemisphereDistribution; + dict[kBlurDict] = mpBlurGraph->getPass("GaussianBlur")->getScriptingDictionary(); + return dict; + } + + RenderPassReflection SSAOPass::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + reflector.addInput(kColorIn, "Color buffer"); + reflector.addOutput(kColorOut, "Color-buffer with AO applied to it"); + reflector.addInput(kDepth, "Depth-buffer"); + reflector.addInput(kNormals, "World space normals, [0, 1] range").flags(RenderPassReflection::Field::Flags::Optional); + reflector.addInternal(kAoMapSize, "AO Map"); + return reflector; + } + + void SSAOPass::compile(RenderContext* pRenderContext, const CompileData& compileData) + { + Fbo::Desc fboDesc; + fboDesc.setColorTarget(0, Falcor::ResourceFormat::R8Unorm); + mpAOFbo = Fbo::create2D(mAoMapSize.x, mAoMapSize.y, fboDesc); + + setKernel(); + setNoiseTexture(mNoiseSize.x, mNoiseSize.y); + + mpBlurGraph = RenderGraph::create("Gaussian Blur"); + GaussianBlurPass::SharedPtr pBlurPass = GaussianBlurPass::create(pRenderContext, mBlurDict); + mpBlurGraph->addPass(pBlurPass, "GaussianBlur"); + mpBlurGraph->markOutput("GaussianBlur.dst"); + mpBlurGraph->setScene(mpScene); + } + + void SSAOPass::execute(RenderContext* pRenderContext, const RenderData& renderData) + { + if (!mpScene) return; + + // Run the AO pass + auto pDepth = renderData[kDepth]->asTexture(); + auto pNormals = renderData[kNormals]->asTexture(); + auto pColorOut = renderData[kColorOut]->asTexture(); + auto pColorIn = renderData[kColorIn]->asTexture(); + auto pAoMap = renderData[kAoMap]->asTexture(); + + assert(pColorOut != pColorIn); + pAoMap = generateAOMap(pRenderContext, mpScene->getCamera().get(), pDepth, pNormals); + + if (mApplyBlur) + { + mpBlurGraph->setInput("GaussianBlur.src", pAoMap); + mpBlurGraph->execute(pRenderContext); + pAoMap = mpBlurGraph->getOutput("GaussianBlur.dst")->asTexture(); + } + + mComposeData.pApplySSAOPass["gColor"] = pColorIn; + mComposeData.pApplySSAOPass["gAOMap"] = pAoMap; + mComposeData.pFbo->attachColorTarget(pColorOut, 0); + mComposeData.pApplySSAOPass->execute(pRenderContext, mComposeData.pFbo); + } + + Texture::SharedPtr SSAOPass::generateAOMap(RenderContext* pContext, const Camera* pCamera, const Texture::SharedPtr& pDepthTexture, const Texture::SharedPtr& pNormalTexture) + { + if (mDirty) + { + ConstantBuffer::SharedPtr pCB = mpSSAOPass["SSAOCB"]; + if (pCB != nullptr) pCB->setBlob(&mData, 0, sizeof(mData)); + mDirty = false; + } + + // Update state/vars + mpSSAOPass["gNoiseSampler"] = mpNoiseSampler; + mpSSAOPass["gTextureSampler"] = mpTextureSampler; + mpSSAOPass["gDepthTex"] = pDepthTexture; + mpSSAOPass["gNoiseTex"] = mpNoiseTexture; + mpSSAOPass["gNormalTex"] = pNormalTexture; + + // Generate AO + mpSSAOPass->execute(pContext, mpAOFbo); + return mpAOFbo->getColorTexture(0); + } + + void SSAOPass::renderUI(Gui::Widgets& widget) + { + uint32_t distribution = (uint32_t)mHemisphereDistribution; + if (widget.dropdown("Kernel Distribution", kDistributionDropdown, distribution)) setDistribution(distribution); + + int32_t size = mData.kernelSize; + if (widget.var("Kernel Size", size, 1, MAX_SAMPLES)) setKernelSize(size); + + float radius = mData.radius; + if (widget.var("Sample Radius", radius, 0.001f, FLT_MAX, 0.001f)) setSampleRadius(radius); + + widget.checkbox("Apply Blur", mApplyBlur); + if (mApplyBlur) + { + auto blurGroup = Gui::Group(widget, "Blur Settings"); + if (blurGroup.open()) + { + mpBlurGraph->getPass("GaussianBlur")->renderUI(blurGroup); + blurGroup.release(); + } + } + } + + void SSAOPass::setSampleRadius(float radius) + { + mData.radius = radius; + mDirty = true; + } + + void SSAOPass::setKernelSize(uint32_t kernelSize) + { + kernelSize = glm::clamp(kernelSize, (uint32_t)1, (uint32_t)MAX_SAMPLES); + mData.kernelSize = kernelSize; + setKernel(); + } + + void SSAOPass::setDistribution(uint32_t distribution) + { + mHemisphereDistribution = (SampleDistribution)distribution; + setKernel(); + } + + void SSAOPass::setKernel() + { + for (uint32_t i = 0; i < mData.kernelSize; i++) + { + // Hemisphere in the Z+ direction + glm::vec3 p; + switch (mHemisphereDistribution) + { + case SampleDistribution::Random: + p = glm::normalize(glm::linearRand(glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f))); + break; + + case SampleDistribution::UniformHammersley: + p = hammersleyUniform(i, mData.kernelSize); + break; + + case SampleDistribution::CosineHammersley: + p = hammersleyCosine(i, mData.kernelSize); + break; + } + + mData.sampleKernel[i] = glm::vec4(p, 0.0f); + + // Skew sample point distance on a curve so more cluster around the origin + float dist = (float)i / (float)mData.kernelSize; + dist = glm::mix(0.1f, 1.0f, dist * dist); + mData.sampleKernel[i] *= dist; + } + + mDirty = true; + } + + void SSAOPass::setNoiseTexture(uint32_t width, uint32_t height) + { + std::vector data; + data.resize(width * height); + + for (uint32_t i = 0; i < width * height; i++) + { + // Random directions on the XY plane + glm::vec2 dir = glm::normalize(glm::linearRand(glm::vec2(-1), glm::vec2(1))) * 0.5f + 0.5f; + data[i] = glm::packUnorm4x8(glm::vec4(dir, 0.0f, 1.0f)); + } + + mpNoiseTexture = Texture::create2D(width, height, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, data.data()); + + mData.noiseScale = glm::vec2(mpAOFbo->getWidth(), mpAOFbo->getHeight()) / glm::vec2(width, height); + + mDirty = true; + } + + SCRIPT_BINDING(SSAOPass) + { + auto c = m.regClass(SSAOPass); + c.func_("kernelRadius", &SSAOPass::setKernelSize); + c.func_("kernelRadius", &SSAOPass::getKernelSize); + c.func_("distribution", &SSAOPass::setDistribution); + c.func_("distribution", &SSAOPass::getDistribution); + c.func_("sampleRadius", &SSAOPass::setSampleRadius); + c.func_("sampleRadius", &SSAOPass::getSampleRadius); + } +} diff --git a/Source/Falcor/Effects/AmbientOcclusion/SSAOPass.h b/Source/Falcor/Effects/AmbientOcclusion/SSAOPass.h new file mode 100644 index 000000000..697effcb9 --- /dev/null +++ b/Source/Falcor/Effects/AmbientOcclusion/SSAOPass.h @@ -0,0 +1,101 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "RenderGraph/RenderGraph.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" +#include "Core/Program/ProgramReflection.h" +#include "Data/Effects/SSAOData.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Effects/Utils/GaussianBlurPass.h" + +namespace Falcor +{ + class dlldecl SSAOPass : public RenderPass + { + public: + using SharedPtr = std::shared_ptr; + static const char* kDesc; + + enum class SampleDistribution + { + Random, + UniformHammersley, + CosineHammersley + }; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + std::string getDesc() override { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override { mpScene = pScene; } + virtual void renderUI(Gui::Widgets& widget) override; + + void setSampleRadius(float radius); + void setKernelSize(uint32_t kernelSize); + void setDistribution(uint32_t distribution); + float getSampleRadius() { return mData.radius; } + uint32_t getKernelSize() { return mData.kernelSize; } + uint32_t getDistribution() { return (uint32_t)mHemisphereDistribution; } + + private: + SSAOPass(); + Texture::SharedPtr generateAOMap(RenderContext* pContext, const Camera* pCamera, const Texture::SharedPtr& pDepthTexture, const Texture::SharedPtr& pNormalTexture); + void setNoiseTexture(uint32_t width, uint32_t height); + void setKernel(); + + SSAOData mData; + bool mDirty = false; + + Fbo::SharedPtr mpAOFbo; + uvec2 mAoMapSize = uvec2(1024); + + Sampler::SharedPtr mpNoiseSampler; + Texture::SharedPtr mpNoiseTexture; + uvec2 mNoiseSize = uvec2(16); + + Sampler::SharedPtr mpTextureSampler; + SampleDistribution mHemisphereDistribution = SampleDistribution::CosineHammersley; + + FullScreenPass::SharedPtr mpSSAOPass; + RenderGraph::SharedPtr mpBlurGraph; + Dictionary mBlurDict; + bool mApplyBlur = true; + + Scene::SharedPtr mpScene; + + struct + { + FullScreenPass::SharedPtr pApplySSAOPass; + Fbo::SharedPtr pFbo; + } mComposeData; + }; +} diff --git a/Source/Falcor/Effects/FXAA/FXAAPass.cpp b/Source/Falcor/Effects/FXAA/FXAAPass.cpp new file mode 100644 index 000000000..db719015e --- /dev/null +++ b/Source/Falcor/Effects/FXAA/FXAAPass.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "FXAAPass.h" + +namespace Falcor +{ + const char* FXAAPass::kDesc = "Fast Approximate Anti-Aliasing"; + + namespace + { + const std::string kSrc = "src"; + const std::string kDst = "dst"; + + const std::string kQualitySubPix = "qualitySubPix"; + const std::string kQualityEdgeThreshold = "qualityEdgeThreshold"; + const std::string kQualityEdgeThresholdMin = "qualityEdgeThresholdMin"; + const std::string kEarlyOut = "earlyOut"; + + const std::string kShaderFilename = "Effects/FXAA.slang"; + } + + FXAAPass::FXAAPass() + { + mpPass = FullScreenPass::create(kShaderFilename); + mpFbo = Fbo::create(); + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); + mpPass["gSampler"] = Sampler::create(samplerDesc); + } + + FXAAPass::SharedPtr FXAAPass::create(RenderContext* pRenderContext, const Dictionary& dict) + { + SharedPtr pFXAA = SharedPtr(new FXAAPass); + for (const auto& v : dict) + { + if (v.key() == kQualitySubPix) pFXAA->mQualitySubPix = v.val(); + if (v.key() == kQualityEdgeThreshold) pFXAA->mQualityEdgeThreshold = v.val(); + if (v.key() == kQualityEdgeThresholdMin) pFXAA->mQualityEdgeThresholdMin = v.val(); + if (v.key() == kEarlyOut) pFXAA->mEarlyOut = v.val(); + else logWarning("Unknown field `" + v.key() + "` in an FXAA dictionary"); + } + return pFXAA; + } + + Dictionary FXAAPass::getScriptingDictionary() + { + Dictionary dict; + dict[kQualitySubPix] = mQualitySubPix; + dict[kQualityEdgeThreshold] = mQualityEdgeThreshold; + dict[kQualityEdgeThresholdMin] = mQualityEdgeThresholdMin; + dict[kEarlyOut] = mEarlyOut; + return dict; + } + + RenderPassReflection FXAAPass::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + reflector.addInput(kSrc, "Source color-buffer"); + reflector.addOutput(kDst, "Destination color-buffer"); + return reflector; + } + + void FXAAPass::execute(RenderContext* pContext, const RenderData& renderData) + { + auto pSrc = renderData[kSrc]->asTexture(); + auto pDst = renderData[kDst]->asTexture(); + mpFbo->attachColorTarget(pDst, 0); + + mpPass["gSrc"] = pSrc; + vec2 rcpFrame = 1.0f / vec2(pSrc->getWidth(), pSrc->getHeight()); + + auto pCB = mpPass["PerFrameCB"]; + pCB["rcpTexDim"] = rcpFrame; + pCB["qualitySubPix"] = mQualitySubPix; + pCB["qualityEdgeThreshold"] = mQualityEdgeThreshold; + pCB["qualityEdgeThresholdMin"] = mQualityEdgeThresholdMin; + pCB["earlyOut"] = mEarlyOut; + + mpPass->execute(pContext, mpFbo); + } + + void FXAAPass::renderUI(Gui::Widgets& widget) + { + widget.var("Sub-Pixel Quality", mQualitySubPix, 0.f, 1.f, 0.001f); + widget.var("Edge Threshold", mQualityEdgeThreshold, 0.f, 1.f, 0.001f); + widget.var("Edge Threshold Min", mQualityEdgeThresholdMin, 0.f, 1.f, 0.001f); + widget.checkbox("Early out", mEarlyOut); + + } + + SCRIPT_BINDING(FXAAPass) + { + auto c = m.regClass(FXAAPass); + c.func_("qualitySubPix", &FXAAPass::setQualitySubPix); + c.func_("qualitySubPix", &FXAAPass::getQualitySubPix); + c.func_("qualityEdgeThreshold", &FXAAPass::setQualityEdgeThreshold); + c.func_("qualityEdgeThreshold", &FXAAPass::getQualityEdgeThreshold); + c.func_("qualityEdgeThresholdMin", &FXAAPass::setQualityEdgeThresholdMin); + c.func_("qualityEdgeThresholdMin", &FXAAPass::getQualityEdgeThresholdMin); + c.func_("earlyOut", &FXAAPass::setEarlyOut); + c.func_("earlyOut", &FXAAPass::getEarlyOut); + } +} diff --git a/Framework/Source/Effects/FXAA/FXAA.h b/Source/Falcor/Effects/FXAA/FXAAPass.h similarity index 57% rename from Framework/Source/Effects/FXAA/FXAA.h rename to Source/Falcor/Effects/FXAA/FXAAPass.h index 5969d61ba..cb1ca95bb 100644 --- a/Framework/Source/Effects/FXAA/FXAA.h +++ b/Source/Falcor/Effects/FXAA/FXAAPass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,66 +26,47 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "Graphics/FullScreenPass.h" -#include "Graphics/Program/ProgramVars.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" namespace Falcor { - class Gui; - class Texture; - class Fbo; - /** Temporal AA class */ - class FXAA : public RenderPass, public inherit_shared_from_this + class dlldecl FXAAPass : public RenderPass { public: - using SharedPtr = std::shared_ptr; + using SharedPtr = std::shared_ptr; static const char* kDesc; - /** Destructor - */ - ~FXAA(); - - /** Create a new instance - */ - static SharedPtr create(const Dictionary& dict = {}); - - /** Render UI controls for this effect. - \param[in] pGui GUI object to render UI elements with - */ - void renderUI(Gui* pGui, const char* uiGroup); - - /** Run the effect - \param[in] pRenderContext Render context with the destination FBO already set - \param[in] pCurColor Current frame color buffer - \param[in] pPrevColor Previous frame color buffer - \param[in] pMotionVec Motion vector buffer - */ - void execute(RenderContext* pRenderContext, const std::shared_ptr& pSrcTex, const std::shared_ptr& pDstFbo); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - /** Get a description of the pass - */ std::string getDesc() override { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; - /** Reflect the pass - */ - virtual RenderPassReflection reflect() const override; + // Scripting functions + void setQualitySubPix(float quality) { mQualitySubPix = quality; } + void setQualityEdgeThreshold(float quality) { mQualityEdgeThreshold = quality; } + void setQualityEdgeThresholdMin(float quality) { mQualityEdgeThresholdMin = quality; } + void setEarlyOut(bool earlyOut) { mEarlyOut = earlyOut; } + float getQualitySubPix() { return mQualitySubPix; } + float getQualityEdgeThreshold() { return mQualityEdgeThreshold; } + float getQualityEdgeThresholdMin() { return mQualityEdgeThresholdMin; } + bool getEarlyOut() { return mEarlyOut; } - /** execute the pass - */ - virtual void execute(RenderContext* pContext, const RenderData* pData) override; private: - FXAA(); + FXAAPass(); - FullScreenPass::UniquePtr mpPass; - GraphicsVars::SharedPtr mpGraphicsVars; - Sampler::SharedPtr mpLinearSampler; + FullScreenPass::SharedPtr mpPass; + Fbo::SharedPtr mpFbo; float mQualitySubPix = 0.75f; float mQualityEdgeThreshold = 0.166f; float mQualityEdgeThresholdMin = 0.0833f; bool mEarlyOut = true; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Effects/Shadows/CSM.cpp b/Source/Falcor/Effects/Shadows/CSM.cpp similarity index 62% rename from Framework/Source/Effects/Shadows/CSM.cpp rename to Source/Falcor/Effects/Shadows/CSM.cpp index ff367caf6..787dcebe7 100644 --- a/Framework/Source/Effects/Shadows/CSM.cpp +++ b/Source/Falcor/Effects/Shadows/CSM.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,23 +25,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "CSM.h" -#include "Graphics/Scene/SceneRenderer.h" -#include "Utils/Math/FalcorMath.h" -#include "Graphics/FboHelper.h" namespace Falcor { const char* CascadedShadowMaps::kDesc = "The pass generates a visibility-map using the CSM technique. The map is for a single light-source.\n" "It supports common filtering modes, including EVSM. It also supports PSSM and SDSM"; - const char* kDepthPassFile = "Effects/DepthPass.slang"; - const char* kShadowPassfile = "Effects/ShadowPass.slang"; - const char* kVisibilityPassFile = "Effects/VisibilityPass.ps.slang"; - const char* kSdsmReadbackLatency = "kSdsmReadbackLatency"; - - const Gui::DropdownList kFilterList = { + namespace + { + const Gui::DropdownList kFilterList = { { (uint32_t)CsmFilterPoint, "Point" }, { (uint32_t)CsmFilterHwPcf, "2x2 HW PCF" }, { (uint32_t)CsmFilterFixedPcf, "Fixed-Size PCF" }, @@ -49,49 +43,68 @@ namespace Falcor { (uint32_t)CsmFilterEvsm2, "EVSM2" }, { (uint32_t)CsmFilterEvsm4, "EVSM4" }, { (uint32_t)CsmFilterStochasticPcf, "Stochastic Poisson PCF" } - }; + }; - const Gui::DropdownList kPartitionList = { - { (uint32_t)CascadedShadowMaps::PartitionMode::Linear, "Linear" }, - { (uint32_t)CascadedShadowMaps::PartitionMode::Logarithmic, "Logarithmic" }, - { (uint32_t)CascadedShadowMaps::PartitionMode::PSSM, "PSSM" } - }; + const Gui::DropdownList kPartitionList = { + { (uint32_t)CascadedShadowMaps::PartitionMode::Linear, "Linear" }, + { (uint32_t)CascadedShadowMaps::PartitionMode::Logarithmic, "Logarithmic" }, + { (uint32_t)CascadedShadowMaps::PartitionMode::PSSM, "PSSM" } + }; - const Gui::DropdownList kMaxAniso = { - { (uint32_t)1, "1" }, - { (uint32_t)2, "2" }, - { (uint32_t)4, "4" }, - { (uint32_t)8, "8" }, - { (uint32_t)16, "16" } - }; + const Gui::DropdownList kMaxAniso = { + { (uint32_t)1, "1" }, + { (uint32_t)2, "2" }, + { (uint32_t)4, "4" }, + { (uint32_t)8, "8" }, + { (uint32_t)16, "16" } + }; + + const std::string kDepth = "depth"; + const std::string kVisibility = "visibility"; + const std::string kBlurPass = "GaussianBlur"; + + const std::string kMapWidth = "mapWidth"; + const std::string kMapHeight = "mapHeight"; + const std::string kVisBufferWidth = "visibilityBufferWidth"; + const std::string kVisBufferHeight = "visibilityBufferHeight"; + const std::string kCascadeCount = "cascadeCount"; + const std::string kVisMapBitsPerChannel = "visibilityMapBitsPerChannel"; + const std::string kBlurDict = "blurDict"; + + const std::string kDepthPassFile = "Effects/DepthPass.slang"; + const std::string kShadowPassfile = "Effects/ShadowPass.slang"; + const std::string kVisibilityPassFile = "Effects/VisibilityPass.ps.slang"; + const std::string kSdsmReadbackLatency = "kSdsmReadbackLatency"; + } +#if 0 class CsmSceneRenderer : public SceneRenderer { public: using UniquePtr = std::unique_ptr; static UniquePtr create(const Scene::SharedConstPtr& pScene, const ProgramReflection::BindLocation& alphaMapCbLoc, const ProgramReflection::BindLocation& alphaMapLoc, const ProgramReflection::BindLocation& alphaMapSamplerLoc) - { - return UniquePtr(new CsmSceneRenderer(pScene, alphaMapCbLoc, alphaMapLoc, alphaMapSamplerLoc)); + { + return UniquePtr(new CsmSceneRenderer(pScene, alphaMapCbLoc, alphaMapLoc, alphaMapSamplerLoc)); } void setDepthClamp(bool enable) { mDepthClamp = enable; } - void renderScene(RenderContext* pContext, const Camera* pCamera) override + void renderScene(RenderContext* pContext, GraphicsState* pState, GraphicsVars* pVars, const Camera* pCamera) override { - pContext->getGraphicsState()->setRasterizerState(nullptr); + pState->setRasterizerState(nullptr); mpLastSetRs = nullptr; - SceneRenderer::renderScene(pContext, pCamera); + SceneRenderer::renderScene(pContext, pState, pVars, pCamera); } protected: CsmSceneRenderer(const Scene::SharedConstPtr& pScene, const ProgramReflection::BindLocation& alphaMapCbLoc, const ProgramReflection::BindLocation& alphaMapLoc, const ProgramReflection::BindLocation& alphaMapSamplerLoc) : SceneRenderer(std::const_pointer_cast(pScene)) - { + { mBindLocations.alphaCB = alphaMapCbLoc; mBindLocations.alphaMap = alphaMapLoc; mBindLocations.alphaMapSampler = alphaMapSamplerLoc; - toggleMeshCulling(false); + toggleMeshCulling(false); Sampler::Desc desc; desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); mpAlphaSampler = Sampler::create(desc); @@ -140,9 +153,9 @@ namespace Falcor if (currentData.pMaterial->getAlphaMode() == AlphaModeMask) { float alphaThreshold = currentData.pMaterial->getAlphaThreshold(); - auto& pDefaultBlock = currentData.pContext->getGraphicsVars()->getDefaultBlock(); + auto& pDefaultBlock = currentData.pVars->getDefaultBlock(); pDefaultBlock->getConstantBuffer(mBindLocations.alphaCB, 0)->setBlob(&alphaThreshold, 0u, sizeof(float)); - if(currentData.pMaterial->getBaseColorTexture()) + if (currentData.pMaterial->getBaseColorTexture()) { pDefaultBlock->setSrv(mBindLocations.alphaMap, 0, currentData.pMaterial->getBaseColorTexture()->getSRV()); } @@ -151,21 +164,22 @@ namespace Falcor pDefaultBlock->setSrv(mBindLocations.alphaMap, 0, nullptr); } pDefaultBlock->setSampler(mBindLocations.alphaMapSampler, 0, mpAlphaSampler); - currentData.pContext->getGraphicsState()->getProgram()->addDefine("TEST_ALPHA"); + currentData.pState->getProgram()->addDefine("TEST_ALPHA"); } else { - currentData.pContext->getGraphicsState()->getProgram()->removeDefine("TEST_ALPHA"); + currentData.pState->getProgram()->removeDefine("TEST_ALPHA"); } const auto& pRsState = getRasterizerState(currentData.pMaterial); - if(pRsState != mpLastSetRs) + if (pRsState != mpLastSetRs) { - currentData.pContext->getGraphicsState()->setRasterizerState(pRsState); + currentData.pState->setRasterizerState(pRsState); mpLastSetRs = pRsState; } return true; }; }; +#endif void createShadowMatrix(const DirectionalLight* pLight, const glm::vec3& center, float radius, glm::mat4& shadowVP) { @@ -180,11 +194,11 @@ namespace Falcor const glm::vec3 lightPos = pLight->getWorldPosition(); const glm::vec3 lookat = pLight->getWorldDirection() + lightPos; glm::vec3 up(0, 1, 0); - if(abs(glm::dot(up, pLight->getWorldDirection())) >= 0.95f) + if (abs(glm::dot(up, pLight->getWorldDirection())) >= 0.95f) { up = glm::vec3(1, 0, 0); } - + glm::mat4 view = glm::lookAt(lightPos, lookat, up); float distFromCenter = glm::length(lightPos - center); float nearZ = max(0.1f, distFromCenter - radius); @@ -197,7 +211,7 @@ namespace Falcor void createShadowMatrix(const Light* pLight, const glm::vec3& center, float radius, float fboAspectRatio, glm::mat4& shadowVP) { - switch(pLight->getType()) + switch (pLight->getType()) { case LightDirectional: return createShadowMatrix((DirectionalLight*)pLight, center, radius, shadowVP); @@ -208,93 +222,6 @@ namespace Falcor } } - CascadedShadowMaps::~CascadedShadowMaps() = default; - - CascadedShadowMaps::CascadedShadowMaps(uint32_t mapWidth, uint32_t mapHeight) - : RenderPass("CascadedShadowMaps") - { - createDepthPassResources(); - createShadowPassResources(mapWidth, mapHeight); - createVisibilityPassResources(); - - mpLightCamera = Camera::create(); - - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Border, Sampler::AddressMode::Border, Sampler::AddressMode::Border).setBorderColor(glm::vec4(1.0f)); - samplerDesc.setLodParams(0.f, 0.f, 0.f); - samplerDesc.setComparisonMode(Sampler::ComparisonMode::LessEqual); - mShadowPass.pPointCmpSampler = Sampler::create(samplerDesc); - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mShadowPass.pLinearCmpSampler = Sampler::create(samplerDesc); - samplerDesc.setComparisonMode(Sampler::ComparisonMode::Disabled); - samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - samplerDesc.setLodParams(-100.f, 100.f, 0.f); - - createVsmSampleState(1); - mpGaussianBlur = GaussianBlur::create(); - mpGaussianBlur->setSigma(2.5f); - mpGaussianBlur->setKernelWidth(5); - } - - void CascadedShadowMaps::setLight(const Light::SharedConstPtr& pLight) - { - mpLight = pLight; - if (mpLight && mpLight->getType() != LightDirectional) - { - setCascadeCount(1); - } - } - - CascadedShadowMaps::SharedPtr CascadedShadowMaps::create(const Light::SharedConstPtr& pLight, uint32_t shadowMapWidth, uint32_t shadowMapHeight, uint32_t visibilityBufferWidth, uint32_t visibilityBufferHeight, const Scene::SharedPtr& pScene, uint32_t cascadeCount, uint32_t visMapBitsPerChannel) - { - CascadedShadowMaps* pCsm = new CascadedShadowMaps(shadowMapWidth, shadowMapHeight); - pCsm->onResize(visibilityBufferWidth, visibilityBufferHeight); - pCsm->setScene(pScene); - pCsm->setCascadeCount(cascadeCount); - pCsm->setVisibilityBufferBitsPerChannel(visMapBitsPerChannel); - pCsm->setLight(pLight); - - return CascadedShadowMaps::SharedPtr(pCsm); - } - - CascadedShadowMaps::SharedPtr CascadedShadowMaps::create(const Dictionary& dict) - { - auto pCSM = SharedPtr(new CascadedShadowMaps()); - for (const auto& v : dict) - { - const std::string& key = v.key(); - if (key == kSdsmReadbackLatency) pCSM->setSdsmReadbackLatency(v.val()); - else logWarning("Unknown field `" + key + "` in a CascadedShadowMaps dictionary"); - } - return pCSM; - } - - void CascadedShadowMaps::setSdsmReadbackLatency(uint32_t latency) - { - if(mSdsmData.readbackLatency != latency) - { - mSdsmData.readbackLatency = latency; - mSdsmData.minMaxReduction = nullptr; - } - } - - void CascadedShadowMaps::createSdsmData(Texture::SharedPtr pTexture) - { - assert(pTexture); - // Only create a new technique if it doesn't exist or the dimensions changed - if (mSdsmData.minMaxReduction) - { - if (mSdsmData.width == pTexture->getWidth() && mSdsmData.height == pTexture->getHeight() && mSdsmData.sampleCount == pTexture->getSampleCount()) - { - return; - } - } - mSdsmData.width = pTexture->getWidth(); - mSdsmData.height = pTexture->getHeight(); - mSdsmData.sampleCount = pTexture->getSampleCount(); - mSdsmData.minMaxReduction = ParallelReduction::create(ParallelReduction::Type::MinMax, mSdsmData.readbackLatency, mSdsmData.width, mSdsmData.height, mSdsmData.sampleCount); - } - void CascadedShadowMaps::createDepthPassResources() { GraphicsProgram::Desc depthPassDesc; @@ -309,9 +236,16 @@ namespace Falcor mDepthPass.pGraphicsVars = GraphicsVars::create(pProg->getReflector()); } - void CascadedShadowMaps::createShadowPassResources(uint32_t mapWidth, uint32_t mapHeight) + void CascadedShadowMaps::createVisibilityPassResources() + { + mVisibilityPass.pFbo = Fbo::create(); + mVisibilityPass.pPass = FullScreenPass::create(kVisibilityPassFile); + mVisibilityPass.mVisualizeCascadesOffset = (uint32_t)mVisibilityPass.pPass->getVars()->getConstantBuffer("PerFrameCB")->getVariableOffset("visualizeCascades"); + } + + void CascadedShadowMaps::createShadowPassResources() { - mShadowPass.mapSize = glm::vec2(float(mapWidth), float(mapHeight)); + mShadowPass.mapSize = glm::vec2(float(mMapWidth), float(mMapHeight)); const ResourceFormat depthFormat = ResourceFormat::D32Float; mCsmData.depthBias = 0.005f; Program::DefineList progDef; @@ -319,7 +253,7 @@ namespace Falcor progDef.add("_CASCADE_COUNT", std::to_string(mCsmData.cascadeCount)); progDef.add("_ALPHA_CHANNEL", "a"); ResourceFormat colorFormat = ResourceFormat::Unknown; - switch(mCsmData.filterMode) + switch (mCsmData.filterMode) { case CsmFilterVsm: colorFormat = ResourceFormat::RG16Float; @@ -339,15 +273,15 @@ namespace Falcor fboDesc.setDepthStencilTarget(depthFormat); uint32_t mipLevels = 1; - if(colorFormat != ResourceFormat::Unknown) + if (colorFormat != ResourceFormat::Unknown) { fboDesc.setColorTarget(0, colorFormat); mipLevels = Texture::kMaxPossible; } - mShadowPass.pFbo = FboHelper::create2D(mapWidth, mapHeight, fboDesc, mCsmData.cascadeCount, mipLevels); - mDepthPass.pState->setFbo(FboHelper::create2D(mapWidth, mapHeight, fboDesc, mCsmData.cascadeCount)); + mShadowPass.pFbo = Fbo::create2D(mMapWidth, mMapHeight, fboDesc, mCsmData.cascadeCount, mipLevels); + mDepthPass.pState->setFbo(Fbo::create2D(mMapWidth, mMapHeight, fboDesc, mCsmData.cascadeCount)); - mShadowPass.fboAspectRatio = (float)mapWidth / (float)mapHeight; + mShadowPass.fboAspectRatio = (float)mMapWidth / (float)mMapHeight; // Create the shadows program GraphicsProgram::Desc shadowPassProgDesc; @@ -362,170 +296,95 @@ namespace Falcor mShadowPass.pGraphicsVars = GraphicsVars::create(pProg->getReflector()); } - void CascadedShadowMaps::setScene(const std::shared_ptr& pScene) + CascadedShadowMaps::CascadedShadowMaps() { - const auto& pReflector = mShadowPass.pGraphicsVars->getReflection(); - const auto& pDefaultBlock = pReflector->getDefaultParameterBlock(); - auto alphaSampler = pDefaultBlock->getResourceBinding("alphaSampler"); - auto alphaMapCB = pDefaultBlock->getResourceBinding("AlphaMapCB"); - auto alphaMap = pDefaultBlock->getResourceBinding("alphaMap"); - mPerLightCbLoc = pDefaultBlock->getResourceBinding("PerLightCB"); + createDepthPassResources(); + createVisibilityPassResources(); - mpCsmSceneRenderer = CsmSceneRenderer::create(pScene, alphaMapCB, alphaMap, alphaSampler); - bool cullMeshes = mpSceneRenderer ? mpSceneRenderer->isMeshCullingEnabled() : true; - mpSceneRenderer = SceneRenderer::create(std::const_pointer_cast(pScene)); - mpSceneRenderer->toggleMeshCulling(cullMeshes); + mpLightCamera = Camera::create(); - setLight(pScene && pScene->getLightCount() ? pScene->getLight(0) : nullptr); - } + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Border, Sampler::AddressMode::Border, Sampler::AddressMode::Border).setBorderColor(glm::vec4(1.0f)); + samplerDesc.setLodParams(0.f, 0.f, 0.f); + samplerDesc.setComparisonMode(Sampler::ComparisonMode::LessEqual); + mShadowPass.pPointCmpSampler = Sampler::create(samplerDesc); + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + mShadowPass.pLinearCmpSampler = Sampler::create(samplerDesc); + samplerDesc.setComparisonMode(Sampler::ComparisonMode::Disabled); + samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + samplerDesc.setLodParams(-100.f, 100.f, 0.f); - void CascadedShadowMaps::createVisibilityPassResources() - { - mVisibilityPass.pState = GraphicsState::create(); - mVisibilityPass.pState->setFbo(Fbo::create()); - mVisibilityPass.pPass = FullScreenPass::create(kVisibilityPassFile); - mVisibilityPass.pGraphicsVars = GraphicsVars::create(mVisibilityPass.pPass->getProgram()->getReflector()); - mVisibilityPass.mVisualizeCascadesOffset = (uint32_t)mVisibilityPass.pGraphicsVars->getConstantBuffer("PerFrameCB")->getVariableOffset("visualizeCascades"); + createVsmSampleState(1);; } - void CascadedShadowMaps::setCascadeCount(uint32_t cascadeCount) + CascadedShadowMaps::SharedPtr CascadedShadowMaps::create(RenderContext* pRenderContext, const Dictionary& dict) { - if(mpLight && (mpLight->getType() != LightDirectional) && (cascadeCount != 1)) + auto pCSM = SharedPtr(new CascadedShadowMaps()); + for (const auto& v : dict) { - logWarning("CascadedShadowMaps::setCascadeCount() - cascadeCount for a non-directional light must be 1"); - cascadeCount = 1; + if (v.key() == kMapWidth) pCSM->mMapWidth = v.val(); + if (v.key() == kMapHeight) pCSM->mMapHeight = v.val(); + if (v.key() == kVisBufferWidth) pCSM->mVisibilityPassData.screenDim.x = v.val(); + if (v.key() == kVisBufferHeight) pCSM->mVisibilityPassData.screenDim.y = v.val(); + if (v.key() == kCascadeCount) pCSM->setCascadeCount(v.val()); + if (v.key() == kVisMapBitsPerChannel) pCSM->setVisibilityBufferBitsPerChannel(v.val()); + if (v.key() == kSdsmReadbackLatency) pCSM->setSdsmReadbackLatency(v.val()); + if (v.key() == kBlurDict) pCSM->mBlurDict = v.val(); + else logWarning("Unknown field `" + v.key() + "` in a CascadedShadowMaps dictionary"); } + pCSM->createShadowPassResources(); + return pCSM; + } - if(mCsmData.cascadeCount != cascadeCount) - { - mCsmData.cascadeCount = cascadeCount; - createShadowPassResources(mShadowPass.pFbo->getWidth(), mShadowPass.pFbo->getHeight()); - } + Dictionary CascadedShadowMaps::getScriptingDictionary() + { + Dictionary dict; + dict[kMapWidth] = mMapWidth; + dict[kMapHeight] = mMapHeight; + dict[kVisBufferWidth] = mVisibilityPassData.screenDim.x; + dict[kVisBufferHeight] = mVisibilityPassData.screenDim.y; + dict[kCascadeCount] = mCsmData.cascadeCount; + dict[kVisMapBitsPerChannel] = mVisibilityPassData.mapBitsPerChannel; + dict[kSdsmReadbackLatency] = mSdsmData.readbackLatency; + dict[kBlurDict] = mpBlurGraph->getPass(kBlurPass)->getScriptingDictionary(); + return dict; } - void CascadedShadowMaps::renderUI(Gui* pGui, const char* uiGroup) + static ResourceFormat getVisBufferFormat(uint32_t bitsPerChannel, bool visualizeCascades) { - if (!uiGroup || pGui->beginGroup(uiGroup)) + switch (bitsPerChannel) { - if (mpLight && mpLight->getType() == LightDirectional) - { - if (pGui->addIntVar("Cascade Count", (int32_t&)mCsmData.cascadeCount, 1, 8)) - { - setCascadeCount(mCsmData.cascadeCount); - } - } - - // Shadow-map size - ivec2 smDims = ivec2(mShadowPass.pFbo->getWidth(), mShadowPass.pFbo->getHeight()); - if (pGui->addInt2Var("Shadow-Map Size", smDims, 0, 8192)) resizeShadowMap(smDims.x, smDims.y); - - // Visibility buffer bits-per channel - static const Gui::DropdownList visBufferBits = - { - {8, "8"}, - {16, "16"}, - {32, "32"} - }; - if (pGui->addDropdown("Visibility Buffer Bits-Per-Channel", visBufferBits, mVisibilityPassData.mapBitsPerChannel)) setVisibilityBufferBitsPerChannel(mVisibilityPassData.mapBitsPerChannel); - - // Mesh culling - bool cullEnabled = isMeshCullingEnabled(); - if (pGui->addCheckBox("Cull Meshes", cullEnabled)) - { - toggleMeshCulling(cullEnabled); - } - - //Filter mode - uint32_t filterIndex = static_cast(mCsmData.filterMode); - if (pGui->addDropdown("Filter Mode", kFilterList, filterIndex)) - { - setFilterMode(filterIndex); - } - - //partition mode - uint32_t newPartitionMode = static_cast(mControls.partitionMode); - - if (pGui->addDropdown("Partition Mode", kPartitionList, newPartitionMode)) - { - mControls.partitionMode = static_cast(newPartitionMode); - } - - if (mControls.partitionMode == PartitionMode::PSSM) - { - pGui->addFloatVar("PSSM Lambda", mControls.pssmLambda, 0, 1.0f); - } - - if (mControls.useMinMaxSdsm == false) - { - pGui->addFloatVar("Min Distance", mControls.distanceRange.x, 0, 1); - pGui->addFloatVar("Max Distance", mControls.distanceRange.y, 0, 1); - } - - pGui->addFloatVar("Cascade Blend Threshold", mCsmData.cascadeBlendThreshold, 0, 1.0f); - pGui->addCheckBox("Depth Clamp", mControls.depthClamp); - - pGui->addFloatVar("Depth Bias", mCsmData.depthBias, 0, FLT_MAX, 0.0001f); - pGui->addCheckBox("Stabilize Cascades", mControls.stabilizeCascades); - - // SDSM data - const char* sdsmGroup = "SDSM MinMax"; - if (pGui->beginGroup(sdsmGroup)) - { - pGui->addCheckBox("Enable", mControls.useMinMaxSdsm); - if(mControls.useMinMaxSdsm) - { - int32_t latency = mSdsmData.readbackLatency; - if (pGui->addIntVar("Readback Latency", latency, 0)) - { - setSdsmReadbackLatency(latency); - } - std::string range = "SDSM Range=[" + std::to_string(mSdsmData.sdsmResult.x) + ", " + std::to_string(mSdsmData.sdsmResult.y) + ']'; - pGui->addText(range.c_str()); - } - pGui->endGroup(); - } - - if (mCsmData.filterMode == CsmFilterFixedPcf || mCsmData.filterMode == CsmFilterStochasticPcf) - { - i32 kernelWidth = mCsmData.pcfKernelWidth; - if (pGui->addIntVar("Kernel Width", kernelWidth, 1, 15, 2)) - { - setPcfKernelWidth(kernelWidth); - } - } - - //VSM/ESM - if (mCsmData.filterMode == CsmFilterVsm || mCsmData.filterMode == CsmFilterEvsm2 || mCsmData.filterMode == CsmFilterEvsm4) - { - const char* vsmGroup = "VSM/EVSM"; - if (pGui->beginGroup(vsmGroup)) - { - uint32_t newMaxAniso = mShadowPass.pVSMTrilinearSampler->getMaxAnisotropy(); - pGui->addDropdown("Max Aniso", kMaxAniso, newMaxAniso); - { - createVsmSampleState(newMaxAniso); - } - - pGui->addFloatVar("Light Bleed Reduction", mCsmData.lightBleedingReduction, 0, 1.0f, 0.01f); + case 8: + return visualizeCascades ? ResourceFormat::RGBA8Unorm : ResourceFormat::R8Unorm; + case 16: + return visualizeCascades ? ResourceFormat::RGBA16Float : ResourceFormat::R16Float; + case 32: + return visualizeCascades ? ResourceFormat::RGBA32Float : ResourceFormat::R32Float; + default: + should_not_get_here(); + return ResourceFormat::Unknown; + } + } - if(mCsmData.filterMode == CsmFilterEvsm2 || mCsmData.filterMode == CsmFilterEvsm4) - { - const char* evsmExpGroup = "EVSM Exp"; - if (pGui->beginGroup(evsmExpGroup)) - { - pGui->addFloatVar("Positive", mCsmData.evsmExponents.x, 0.0f, 5.54f, 0.01f); - pGui->addFloatVar("Negative", mCsmData.evsmExponents.y, 0.0f, 5.54f, 0.01f); - pGui->endGroup(); - } - } + RenderPassReflection CascadedShadowMaps::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + reflector.addOutput(kVisibility, "Visibility map. Values are [0,1] where 0 means the pixel is completely shadowed and 1 means it's not shadowed at all") + .format(getVisBufferFormat(mVisibilityPassData.mapBitsPerChannel, mVisibilityPassData.shouldVisualizeCascades)) + .texture2D(mVisibilityPassData.screenDim.x, mVisibilityPassData.screenDim.y); + reflector.addInput(kDepth, "Pre-initialized scene depth buffer used for SDSM.\nIf not provided, the pass will run a depth-pass internally").flags(RenderPassReflection::Field::Flags::Optional); + return reflector; + } - mpGaussianBlur->renderUI(pGui, "Blur"); - pGui->endGroup(); - } - } + void CascadedShadowMaps::compile(RenderContext* pContext, const CompileData& compileData) + { + mpBlurGraph = RenderGraph::create("Gaussian Blur"); + GaussianBlurPass::SharedPtr pBlurPass = GaussianBlurPass::create(pContext, mBlurDict); + mpBlurGraph->addPass(pBlurPass, kBlurPass); + mpBlurGraph->markOutput(kBlurPass + ".dst"); + mpBlurGraph->setScene(mpScene ? mpScene : nullptr); - if(uiGroup) pGui->endGroup(); - } + mVisibilityPass.pFbo->attachColorTarget(nullptr, 0); } void camClipSpaceToWorldSpace(const Camera* pCamera, glm::vec3 viewFrustum[8], glm::vec3& center, float& radius) @@ -545,7 +404,7 @@ namespace Falcor glm::mat4 invViewProj = pCamera->getInvViewProjMatrix(); center = glm::vec3(0, 0, 0); - for(uint32_t i = 0; i < 8; i++) + for (uint32_t i = 0; i < 8; i++) { glm::vec4 crd = invViewProj * glm::vec4(clipSpace[i], 1); viewFrustum[i] = glm::vec3(crd) / crd.w; @@ -556,7 +415,7 @@ namespace Falcor // Calculate bounding sphere radius radius = 0; - for(uint32_t i = 0; i < 8; i++) + for (uint32_t i = 0; i < 8; i++) { float d = glm::length(center - viewFrustum[i]); radius = max(d, radius); @@ -588,7 +447,7 @@ namespace Falcor // Transform the frustum into light clip-space and calculate min-max glm::vec4 maxCS(-1, -1, 0, 1); glm::vec4 minCS(1, 1, 1, 1); - for(uint32_t i = 0; i < 8; i++) + for (uint32_t i = 0; i < 8; i++) { glm::vec4 c = lightVP * glm::vec4(crd[i], 1.0f); c /= c.w; @@ -621,7 +480,7 @@ namespace Falcor // Create the global shadow space createShadowMatrix(mpLight.get(), camFrustum.center, camFrustum.radius, mShadowPass.fboAspectRatio, mCsmData.globalMat); - if(mCsmData.cascadeCount == 1) + if (mCsmData.cascadeCount == 1) { mCsmData.cascadeScale[0] = glm::vec4(1); mCsmData.cascadeOffset[0] = glm::vec4(0); @@ -636,11 +495,11 @@ namespace Falcor float nextCascadeStart = distanceRange.x; - for(int32_t c = 0; c < mCsmData.cascadeCount; c++) + for (int32_t c = 0; c < mCsmData.cascadeCount; c++) { float cascadeStart = nextCascadeStart; - switch(mControls.partitionMode) + switch (mControls.partitionMode) { case PartitionMode::Linear: nextCascadeStart = cascadeStart + (distanceRange.y - distanceRange.x) / float(mCsmData.cascadeCount); @@ -674,7 +533,7 @@ namespace Falcor // Calculate the cascade frustum glm::vec3 cascadeFrust[8]; - for(uint32_t i = 0; i < 4; i++) + for (uint32_t i = 0; i < 4; i++) { glm::vec3 edge = camFrustum.crd[i + 4] - camFrustum.crd[i]; glm::vec3 start = edge * cascadeStart; @@ -721,44 +580,26 @@ namespace Falcor pCB->setBlob(&mCsmData, 0, sizeof(mCsmData)); - pCtx->pushGraphicsVars(mShadowPass.pGraphicsVars); - pCtx->pushGraphicsState(mShadowPass.pState); mpLightCamera->setProjectionMatrix(mCsmData.globalMat); - mpCsmSceneRenderer->renderScene(pCtx, mpLightCamera.get()); - pCtx->popGraphicsState(); - pCtx->popGraphicsVars(); + mpScene->render(pCtx, mShadowPass.pState.get(), mShadowPass.pGraphicsVars.get()); +// mpCsmSceneRenderer->renderScene(pCtx, mShadowPass.pState.get(), mShadowPass.pGraphicsVars.get(), mpLightCamera.get()); } void CascadedShadowMaps::executeDepthPass(RenderContext* pCtx, const Camera* pCamera) { - // Must have an FBO attached, otherwise don't know the size of the depth map - const auto& pStateFbo = pCtx->getGraphicsState()->getFbo(); - uint32_t width, height; - if (pStateFbo) - { - width = pStateFbo->getWidth(); - height = pStateFbo->getHeight(); - } - else - { - width = (uint32_t)mShadowPass.mapSize.x; - height = (uint32_t)mShadowPass.mapSize.y; - } + uint32_t width = (uint32_t)mShadowPass.mapSize.x; + uint32_t height = (uint32_t)mShadowPass.mapSize.y; Fbo::SharedConstPtr pFbo = mDepthPass.pState->getFbo(); - if((pFbo == nullptr) || (pFbo->getWidth() != width) || (pFbo->getHeight() != height)) + if ((pFbo == nullptr) || (pFbo->getWidth() != width) || (pFbo->getHeight() != height)) { Fbo::Desc desc; desc.setDepthStencilTarget(mShadowPass.pFbo->getDepthStencilTexture()->getFormat()); - mDepthPass.pState->setFbo(FboHelper::create2D(width, height, desc)); + mDepthPass.pState->setFbo(Fbo::create2D(width, height, desc)); } pCtx->clearFbo(pFbo.get(), glm::vec4(), 1, 0, FboAttachmentType::Depth); - pCtx->pushGraphicsState(mDepthPass.pState); - pCtx->pushGraphicsVars(mDepthPass.pGraphicsVars); - mpCsmSceneRenderer->renderScene(pCtx, pCamera); - pCtx->popGraphicsVars(); - pCtx->popGraphicsState(); +// mpCsmSceneRenderer->renderScene(pCtx, mDepthPass.pState.get(), mDepthPass.pGraphicsVars.get(), pCamera); } void CascadedShadowMaps::createVsmSampleState(uint32_t maxAnisotropy) @@ -772,7 +613,7 @@ namespace Falcor void CascadedShadowMaps::reduceDepthSdsmMinMax(RenderContext* pRenderCtx, const Camera* pCamera, Texture::SharedPtr pDepthBuffer) { - if(pDepthBuffer == nullptr) + if (pDepthBuffer == nullptr) { // Run a shadow pass executeDepthPass(pRenderCtx, pCamera); @@ -784,7 +625,7 @@ namespace Falcor // Convert to linear glm::mat4 camProj = pCamera->getProjMatrix(); - distanceRange = camProj[2][2] - distanceRange*camProj[2][3]; + distanceRange = camProj[2][2] - distanceRange * camProj[2][3]; distanceRange = camProj[3][2] / distanceRange; distanceRange = (distanceRange - pCamera->getNearPlane()) / (pCamera->getFarPlane() - pCamera->getNearPlane()); distanceRange = glm::clamp(distanceRange, glm::vec2(0), glm::vec2(1)); @@ -800,7 +641,7 @@ namespace Falcor vec2 CascadedShadowMaps::calcDistanceRange(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pDepthBuffer) { - if(mControls.useMinMaxSdsm) + if (mControls.useMinMaxSdsm) { reduceDepthSdsmMinMax(pRenderCtx, pCamera, pDepthBuffer); return mSdsmData.sdsmResult; @@ -811,22 +652,68 @@ namespace Falcor } } - Texture::SharedPtr CascadedShadowMaps::generateVisibilityBuffer(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pDepthBuffer) + void CascadedShadowMaps::setDataIntoVars(const GraphicsVars::SharedPtr& pVars, const std::string& varName) + { + switch (mCsmData.filterMode) + { + case CsmFilterPoint: + pVars[varName + ".shadowMap"] = mShadowPass.pFbo->getDepthStencilTexture(); + pVars["gCsmCompareSampler"] = mShadowPass.pPointCmpSampler; + break; + case CsmFilterHwPcf: + case CsmFilterFixedPcf: + case CsmFilterStochasticPcf: + pVars[varName + ".shadowMap"] = mShadowPass.pFbo->getDepthStencilTexture(); + pVars["gCsmCompareSampler"] = mShadowPass.pLinearCmpSampler; + break; + case CsmFilterVsm: + case CsmFilterEvsm2: + case CsmFilterEvsm4: + pVars[varName + ".shadowMap"] = mShadowPass.pFbo->getColorTexture(0); + pVars[varName + ".csmSampler"] = mShadowPass.pVSMTrilinearSampler; + break; + } + + mCsmData.lightDir = glm::normalize(((DirectionalLight*)mpLight.get())->getWorldDirection()); + ConstantBuffer::SharedPtr pCB = pVars["PerFrameCB"]; + size_t offset = pCB->getVariableOffset(varName + ".globalMat"); + pCB->setBlob(&mCsmData, offset, sizeof(mCsmData)); + } + + void CascadedShadowMaps::setupVisibilityPassFbo(const Texture::SharedPtr& pVisBuffer) { - setupVisibilityPassFbo(nullptr); - executeInternal(pRenderCtx, pCamera, pDepthBuffer); - return mVisibilityPass.pState->getFbo()->getColorTexture(0); + Texture::SharedPtr pTex = mVisibilityPass.pFbo->getColorTexture(0); + bool rebind = false; + + if (pVisBuffer && (pVisBuffer != pTex)) + { + rebind = true; + pTex = pVisBuffer; + } + else if (pTex == nullptr) + { + rebind = true; + ResourceFormat format = getVisBufferFormat(mVisibilityPassData.mapBitsPerChannel, mVisibilityPassData.shouldVisualizeCascades); + pTex = Texture::create2D(mVisibilityPassData.screenDim.x, mVisibilityPassData.screenDim.y, format, 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); + } + + if (rebind) mVisibilityPass.pFbo->attachColorTarget(pTex, 0); } - void CascadedShadowMaps::executeInternal(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pSceneDepthBuffer) + void CascadedShadowMaps::execute(RenderContext* pContext, const RenderData& renderData) { - if (!mpLight || !mpSceneRenderer) return; + if (!mpLight || !mpScene) return; + + setupVisibilityPassFbo(renderData[kVisibility]->asTexture()); + const auto& pDepth = renderData[kDepth]->asTexture(); + const auto pCamera = mpScene->getCamera().get(); + //const auto pCamera = mpCsmSceneRenderer->getScene()->getActiveCamera().get(); const glm::vec4 clearColor(0); - pRenderCtx->clearFbo(mShadowPass.pFbo.get(), clearColor, 1, 0, FboAttachmentType::All); + pContext->clearFbo(mShadowPass.pFbo.get(), clearColor, 1, 0, FboAttachmentType::All); // Calc the bounds - glm::vec2 distanceRange = calcDistanceRange(pRenderCtx, pCamera, pSceneDepthBuffer); + glm::vec2 distanceRange = calcDistanceRange(pContext, pCamera, pDepth); GraphicsState::Viewport VP; VP.originX = 0; @@ -838,153 +725,234 @@ namespace Falcor //Set shadow pass state mShadowPass.pState->setViewport(0, VP); - mpCsmSceneRenderer->setDepthClamp(mControls.depthClamp); - pRenderCtx->pushGraphicsState(mShadowPass.pState); + /*mpCsmSceneRenderer->setDepthClamp(mControls.depthClamp);*/ partitionCascades(pCamera, distanceRange); - renderScene(pRenderCtx); - - if(mCsmData.filterMode == CsmFilterVsm || mCsmData.filterMode == CsmFilterEvsm2 || mCsmData.filterMode == CsmFilterEvsm4) + renderScene(pContext); + + if (mCsmData.filterMode == CsmFilterVsm || mCsmData.filterMode == CsmFilterEvsm2 || mCsmData.filterMode == CsmFilterEvsm4) { - mpGaussianBlur->execute(pRenderCtx, mShadowPass.pFbo->getColorTexture(0), mShadowPass.pFbo); - mShadowPass.pFbo->getColorTexture(0)->generateMips(pRenderCtx); + mpBlurGraph->setInput(kBlurPass + ".src", mShadowPass.pFbo->getColorTexture(0)); + mpBlurGraph->execute(pContext); + mShadowPass.pFbo->attachColorTarget(mpBlurGraph->getOutput(kBlurPass + ".dst")->asTexture(), 0); + mShadowPass.pFbo->getColorTexture(0)->generateMips(pContext); } - pRenderCtx->popGraphicsState(); // Clear visibility buffer - auto pFbo = mVisibilityPass.pState->getFbo().get(); - pRenderCtx->clearFbo(pFbo, glm::vec4(1, 0, 0, 0), 1, 0, FboAttachmentType::All); + pContext->clearFbo(mVisibilityPass.pFbo.get(), glm::vec4(1, 0, 0, 0), 1, 0, FboAttachmentType::All); // Update Vars - mVisibilityPass.pGraphicsVars->setTexture("gDepth", pSceneDepthBuffer ? pSceneDepthBuffer : mDepthPass.pState->getFbo()->getDepthStencilTexture()); - setDataIntoGraphicsVars(mVisibilityPass.pGraphicsVars, "gCsmData"); - auto pCb = mVisibilityPass.pGraphicsVars->getConstantBuffer("PerFrameCB"); + mVisibilityPass.pPass["gDepth"] = pDepth ? pDepth : mDepthPass.pState->getFbo()->getDepthStencilTexture(); + setDataIntoVars(mVisibilityPass.pPass->getVars(), "gCsmData"); mVisibilityPassData.camInvViewProj = pCamera->getInvViewProjMatrix(); - mVisibilityPassData.screenDim = glm::uvec2(pFbo->getWidth(), pFbo->getHeight()); - pCb->setBlob(&mVisibilityPassData, mVisibilityPass.mVisualizeCascadesOffset, sizeof(mVisibilityPassData)); + mVisibilityPassData.screenDim = glm::uvec2(mVisibilityPass.pFbo->getWidth(), mVisibilityPass.pFbo->getHeight()); + mVisibilityPass.pPass["PerFrameCB"].setBlob(mVisibilityPassData, mVisibilityPass.mVisualizeCascadesOffset); // Render visibility buffer - pRenderCtx->pushGraphicsState(mVisibilityPass.pState); - pRenderCtx->pushGraphicsVars(mVisibilityPass.pGraphicsVars); - mVisibilityPass.pPass->execute(pRenderCtx); - pRenderCtx->popGraphicsVars(); - pRenderCtx->popGraphicsState(); + mVisibilityPass.pPass->execute(pContext, mVisibilityPass.pFbo); } - void CascadedShadowMaps::setDataIntoGraphicsVars(GraphicsVars::SharedPtr pVars, const std::string& varName) - { - switch (mCsmData.filterMode) - { - case CsmFilterPoint: - pVars->setTexture(varName + ".shadowMap", mShadowPass.pFbo->getDepthStencilTexture()); - pVars->setSampler("gCsmCompareSampler", mShadowPass.pPointCmpSampler); - break; - case CsmFilterHwPcf: - case CsmFilterFixedPcf: - case CsmFilterStochasticPcf: - pVars->setTexture(varName + ".shadowMap", mShadowPass.pFbo->getDepthStencilTexture()); - pVars->setSampler("gCsmCompareSampler", mShadowPass.pLinearCmpSampler); - break; - case CsmFilterVsm: - case CsmFilterEvsm2: - case CsmFilterEvsm4: - pVars->setTexture(varName + ".shadowMap", mShadowPass.pFbo->getColorTexture(0)); - pVars->setSampler(varName + ".csmSampler", mShadowPass.pVSMTrilinearSampler); - break; - } - - mCsmData.lightDir = glm::normalize(((DirectionalLight*)mpLight.get())->getWorldDirection()); - ConstantBuffer::SharedPtr pCB = pVars->getConstantBuffer("PerFrameCB"); - size_t offset = pCB->getVariableOffset(varName + ".globalMat"); - pCB->setBlob(&mCsmData, offset, sizeof(mCsmData)); - } - - Texture::SharedPtr CascadedShadowMaps::getShadowMap() const + void CascadedShadowMaps::setLight(const Light::SharedConstPtr& pLight) { - switch(mCsmData.filterMode) + mpLight = pLight; + if (mpLight && mpLight->getType() != LightDirectional) { - case CsmFilterVsm: - case CsmFilterEvsm2: - case CsmFilterEvsm4: - return mShadowPass.pFbo->getColorTexture(0); + setCascadeCount(1); } - return mShadowPass.pFbo->getDepthStencilTexture(); } - void CascadedShadowMaps::setFilterMode(uint32_t newFilterMode) + void CascadedShadowMaps::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) { - mCsmData.filterMode = newFilterMode; - createShadowPassResources(mShadowPass.pFbo->getWidth(), mShadowPass.pFbo->getHeight()); + const auto& pReflector = mShadowPass.pGraphicsVars->getReflection(); + const auto& pDefaultBlock = pReflector->getDefaultParameterBlock(); + auto alphaSampler = pDefaultBlock->getResourceBinding("alphaSampler"); + auto alphaMapCB = pDefaultBlock->getResourceBinding("AlphaMapCB"); + auto alphaMap = pDefaultBlock->getResourceBinding("alphaMap"); + mPerLightCbLoc = pDefaultBlock->getResourceBinding("PerLightCB"); + + mpScene = pScene; + toggleMeshCulling(mCullMeshes); + setLight(pScene && pScene->getLightCount() ? pScene->getLight(0) : nullptr); + +// mpCsmSceneRenderer = CsmSceneRenderer::create(pScene, alphaMapCB, alphaMap, alphaSampler); +// mpCsmSceneRenderer->toggleMeshCulling(mCullMeshes); +// setLight(pScene && pScene->getLightCount() ? pScene->getLight(0) : nullptr); } - void CascadedShadowMaps::setEvsmBlur(uint32_t kernelWidth, float sigma) + void CascadedShadowMaps::renderUI(Gui::Widgets& widget) { - mpGaussianBlur->setKernelWidth(kernelWidth); - mpGaussianBlur->setSigma(sigma); - } + if (mpLight && mpLight->getType() == LightDirectional) + { + if (widget.var("Cascade Count", (int32_t&)mCsmData.cascadeCount, 1, 8)) + { + setCascadeCount(mCsmData.cascadeCount); + } + } - void CascadedShadowMaps::toggleMeshCulling(bool enabled) - { - mpCsmSceneRenderer->toggleMeshCulling(enabled); + // Shadow-map size + ivec2 smDims = ivec2(mShadowPass.pFbo->getWidth(), mShadowPass.pFbo->getHeight()); + if (widget.var("Shadow-Map Size", smDims, 0, 8192)) resizeShadowMap(smDims.x, smDims.y); + + // Visibility buffer bits-per channel + static const Gui::DropdownList visBufferBits = + { + {8, "8"}, + {16, "16"}, + {32, "32"} + }; + if (widget.dropdown("Visibility Buffer Bits-Per-Channel", visBufferBits, mVisibilityPassData.mapBitsPerChannel)) setVisibilityBufferBitsPerChannel(mVisibilityPassData.mapBitsPerChannel); + + // Mesh culling + if (widget.checkbox("Cull Meshes", mCullMeshes)) toggleMeshCulling(mCullMeshes); + + //Filter mode + uint32_t filterIndex = static_cast(mCsmData.filterMode); + if (widget.dropdown("Filter Mode", kFilterList, filterIndex)) + { + setFilterMode(filterIndex); + } + + //partition mode + uint32_t newPartitionMode = static_cast(mControls.partitionMode); + + if (widget.dropdown("Partition Mode", kPartitionList, newPartitionMode)) + { + setPartitionMode(newPartitionMode); + } + + if (mControls.partitionMode == PartitionMode::PSSM) + { + widget.var("PSSM Lambda", mControls.pssmLambda, 0.f, 1.0f); + } + + if (mControls.useMinMaxSdsm == false) + { + widget.var("Min Distance", mControls.distanceRange.x, 0.f, 1.f, 0.001f); + widget.var("Max Distance", mControls.distanceRange.y, 0.f, 1.f, 0.001f); + } + + widget.var("Cascade Blend Threshold", mCsmData.cascadeBlendThreshold, 0.f, 1.0f, 0.001f); + widget.checkbox("Depth Clamp", mControls.depthClamp); + + widget.var("Depth Bias", mCsmData.depthBias, 0.f, FLT_MAX, 0.0001f); + widget.checkbox("Stabilize Cascades", mControls.stabilizeCascades); + + // SDSM data + auto sdsmGroup = Gui::Group(widget, "SDSM MinMax"); + if (sdsmGroup.open()) + { + sdsmGroup.checkbox("Enable", mControls.useMinMaxSdsm); + if (mControls.useMinMaxSdsm) + { + int32_t latency = mSdsmData.readbackLatency; + if (sdsmGroup.var("Readback Latency", latency, 0)) + { + setSdsmReadbackLatency(latency); + } + std::string range = "SDSM Range=[" + std::to_string(mSdsmData.sdsmResult.x) + ", " + std::to_string(mSdsmData.sdsmResult.y) + ']'; + sdsmGroup.text(range.c_str()); + } + + sdsmGroup.release(); + } + + + if (mCsmData.filterMode == CsmFilterFixedPcf || mCsmData.filterMode == CsmFilterStochasticPcf) + { + i32 kernelWidth = mCsmData.pcfKernelWidth; + if (widget.var("Kernel Width", kernelWidth, 1, 15, 2)) + { + setPcfKernelWidth(kernelWidth); + } + } + + //VSM/ESM + if (mCsmData.filterMode == CsmFilterVsm || mCsmData.filterMode == CsmFilterEvsm2 || mCsmData.filterMode == CsmFilterEvsm4) + { + auto vsmGroup = Gui::Group(widget, "VSM/EVSM"); + if (vsmGroup.open()) + { + uint32_t newMaxAniso = mShadowPass.pVSMTrilinearSampler->getMaxAnisotropy(); + vsmGroup.dropdown("Max Aniso", kMaxAniso, newMaxAniso); + { + createVsmSampleState(newMaxAniso); + } + + vsmGroup.var("Light Bleed Reduction", mCsmData.lightBleedingReduction, 0.f, 1.0f, 0.01f); + + if (mCsmData.filterMode == CsmFilterEvsm2 || mCsmData.filterMode == CsmFilterEvsm4) + { + auto evsmExpGroup = Gui::Group(widget, "EVSM Exp"); + if (evsmExpGroup.open()) + { + evsmExpGroup.var("Positive", mCsmData.evsmExponents.x, 0.0f, 5.54f, 0.01f); + evsmExpGroup.var("Negative", mCsmData.evsmExponents.y, 0.0f, 5.54f, 0.01f); + evsmExpGroup.release(); + } + } + + auto blurGroup = Gui::Group(widget, "Blur Settings"); + if (blurGroup.open()) + { + mpBlurGraph->getPass(kBlurPass)->renderUI(blurGroup); + blurGroup.release(); + } + + vsmGroup.release(); + } + } } - bool CascadedShadowMaps::isMeshCullingEnabled() const + void CascadedShadowMaps::onResize(uint32_t width, uint32_t height) { - return mpCsmSceneRenderer->isMeshCullingEnabled(); + mVisibilityPassData.screenDim = { width, height }; + mVisibilityPass.pFbo->attachColorTarget(nullptr, 0); } - static ResourceFormat getVisBufferFormat(uint32_t bitsPerChannel, bool visualizeCascades) + void CascadedShadowMaps::setSdsmReadbackLatency(uint32_t latency) { - switch (bitsPerChannel) + if (mSdsmData.readbackLatency != latency) { - case 8: - return visualizeCascades ? ResourceFormat::RGBA8Unorm : ResourceFormat::R8Unorm; - case 16: - return visualizeCascades ? ResourceFormat::RGBA16Float : ResourceFormat::R16Float; - case 32: - return visualizeCascades ? ResourceFormat::RGBA32Float : ResourceFormat::R32Float; - default: - should_not_get_here(); - return ResourceFormat::Unknown; + mSdsmData.readbackLatency = latency; + mSdsmData.minMaxReduction = nullptr; } } - void CascadedShadowMaps::setupVisibilityPassFbo(const Texture::SharedPtr& pVisBuffer) + void CascadedShadowMaps::createSdsmData(Texture::SharedPtr pTexture) { - const Fbo::SharedPtr& pFbo = mVisibilityPass.pState->getFbo(); - Texture::SharedPtr pTex = pFbo->getColorTexture(0); - bool rebind = false; - - if (pVisBuffer && (pVisBuffer != pTex)) + assert(pTexture); + // Only create a new technique if it doesn't exist or the dimensions changed + if (mSdsmData.minMaxReduction) { - rebind = true; - pTex = pVisBuffer; + if (mSdsmData.width == pTexture->getWidth() && mSdsmData.height == pTexture->getHeight() && mSdsmData.sampleCount == pTexture->getSampleCount()) + { + return; + } } - else if (pTex == nullptr) + mSdsmData.width = pTexture->getWidth(); + mSdsmData.height = pTexture->getHeight(); + mSdsmData.sampleCount = pTexture->getSampleCount(); + mSdsmData.minMaxReduction = ParallelReduction::create(ParallelReduction::Type::MinMax, mSdsmData.readbackLatency, mSdsmData.width, mSdsmData.height, mSdsmData.sampleCount); + } + + void CascadedShadowMaps::setCascadeCount(uint32_t cascadeCount) + { + if (mpLight && (mpLight->getType() != LightDirectional) && (cascadeCount != 1)) { - rebind = true; - ResourceFormat format = getVisBufferFormat(mVisibilityPassData.mapBitsPerChannel, mVisibilityPassData.shouldVisualizeCascades); - pTex = Texture::create2D(mVisibilityPassData.screenDim.x, mVisibilityPassData.screenDim.y, format, 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); + logWarning("CascadedShadowMaps::setCascadeCount() - cascadeCount for a non-directional light must be 1"); + cascadeCount = 1; } - if (rebind) + if (mCsmData.cascadeCount != cascadeCount) { - pFbo->attachColorTarget(pTex, 0); - mVisibilityPass.pState->setFbo(pFbo); + mCsmData.cascadeCount = cascadeCount; + createShadowPassResources(); } } - void CascadedShadowMaps::toggleCascadeVisualization(bool shouldVisualze) - { - mVisibilityPassData.shouldVisualizeCascades = shouldVisualze; - mVisibilityPass.pState->getFbo()->attachColorTarget(nullptr, 0); - mPassChangedCB(); - } - - void CascadedShadowMaps::onResize(uint32_t width, uint32_t height) + void CascadedShadowMaps::setFilterMode(uint32_t newFilterMode) { - mVisibilityPassData.screenDim = uvec2(width, height); - mVisibilityPass.pState->getFbo()->attachColorTarget(nullptr, 0); - mPassChangedCB(); + mCsmData.filterMode = newFilterMode; + createShadowPassResources(); } void CascadedShadowMaps::setVisibilityBufferBitsPerChannel(uint32_t bitsPerChannel) @@ -995,35 +963,70 @@ namespace Falcor return; } mVisibilityPassData.mapBitsPerChannel = bitsPerChannel; - mVisibilityPass.pState->getFbo()->attachColorTarget(nullptr, 0); + mVisibilityPass.pFbo->attachColorTarget(nullptr, 0); mPassChangedCB(); } - static const std::string kDepth = "depth"; - static const std::string kVisibility = "visibility"; - - RenderPassReflection CascadedShadowMaps::reflect() const + void CascadedShadowMaps::setMapWidth(uint32_t width) { - RenderPassReflection reflector; - - reflector.addOutput(kVisibility, "Visibility map. Values are [0,1] where 0 means the pixel is completely shadowed and 1 means it's not shadowed at all") - .format(getVisBufferFormat(mVisibilityPassData.mapBitsPerChannel, mVisibilityPassData.shouldVisualizeCascades)) - .texture2D(mVisibilityPassData.screenDim.x, mVisibilityPassData.screenDim.y); + mMapWidth = width; + createShadowPassResources(); + } - reflector.addInput(kDepth, "Pre-initialized scene depth buffer used for SDSM.\nIf not provided, the pass will run a depth-pass internally").flags(RenderPassReflection::Field::Flags::Optional); + void CascadedShadowMaps::setMapHeight(uint32_t height) + { + mMapHeight = height; + createShadowPassResources(); + } - return reflector; + void CascadedShadowMaps::resizeShadowMap(uint32_t width, uint32_t height) + { + mMapWidth = width; + mMapHeight = height; + createShadowPassResources(); } - void CascadedShadowMaps::execute(RenderContext* pContext, const RenderData* pRenderData) + void CascadedShadowMaps::toggleMeshCulling(bool enabled) { - setupVisibilityPassFbo(pRenderData->getTexture(kVisibility)); - const auto& pDepth = pRenderData->getTexture(kDepth); - executeInternal(pContext, mpSceneRenderer->getScene()->getActiveCamera().get(), pDepth); + mCullMeshes = enabled; } - void CascadedShadowMaps::resizeShadowMap(uint32_t width, uint32_t height) + SCRIPT_BINDING(CascadedShadowMaps) { - createShadowPassResources(width, height); + auto c = m.regClass(CascadedShadowMaps); + c.func_("cascadeCount", &CascadedShadowMaps::setCascadeCount); + c.func_("cascadeCount", &CascadedShadowMaps::getCascadeCount); + c.func_("mapWidth", &CascadedShadowMaps::setMapWidth); + c.func_("mapWidth", &CascadedShadowMaps::getMapWidth); + c.func_("mapHeight", &CascadedShadowMaps::setMapHeight); + c.func_("mapHeight", &CascadedShadowMaps::getMapHeight); + c.func_("visibilityBufferChannelBits", &CascadedShadowMaps::setVisibilityBufferBitsPerChannel); + c.func_("visibilityBufferChannelBits", &CascadedShadowMaps::getVisibilityBufferBitsPerChannel); + c.func_("filter", &CascadedShadowMaps::setFilterMode); + c.func_("filter", &CascadedShadowMaps::getFilterMode); + c.func_("sdsmLatency", &CascadedShadowMaps::setSdsmReadbackLatency); + c.func_("sdsmLatency", &CascadedShadowMaps::getSdsmReadbackLatency); + c.func_("partition", &CascadedShadowMaps::setPartitionMode); + c.func_("partition", &CascadedShadowMaps::getPartitionMode); + c.func_("lambda", &CascadedShadowMaps::setPSSMLambda); + c.func_("lambda", &CascadedShadowMaps::getPSSMLambda); + c.func_("minDistance", &CascadedShadowMaps::setMinDistanceRange); + c.func_("minDistance", &CascadedShadowMaps::getMinDistanceRange); + c.func_("maxDistance", &CascadedShadowMaps::setMaxDistanceRange); + c.func_("maxDistance", &CascadedShadowMaps::getMaxDistanceRange); + c.func_("cascadeThreshold", &CascadedShadowMaps::setCascadeBlendThreshold); + c.func_("cascadeThreshold", &CascadedShadowMaps::getCascadeBlendThreshold); + c.func_("depthBias", &CascadedShadowMaps::setDepthBias); + c.func_("depthBias", &CascadedShadowMaps::getDepthBias); + c.func_("kernelWidth", &CascadedShadowMaps::setPcfKernelWidth); + c.func_("kernelWidth", &CascadedShadowMaps::getPcfKernelWidth); + c.func_("maxAniso", &CascadedShadowMaps::setVsmMaxAnisotropy); + c.func_("maxAniso", &CascadedShadowMaps::getVsmMaxAnisotropy); + c.func_("bleedReduction", &CascadedShadowMaps::setVsmLightBleedReduction); + c.func_("bleedReduction", &CascadedShadowMaps::getVsmLightBleedReduction); + c.func_("positiveExp", &CascadedShadowMaps::setEvsmPositiveExponent); + c.func_("positiveExp", &CascadedShadowMaps::getEvsmPositiveExponent); + c.func_("negativeExp", &CascadedShadowMaps::setEvsmNegativeExponent); + c.func_("negativeExp", &CascadedShadowMaps::getEvsmNegativeExponent); } } diff --git a/Framework/Source/Effects/Shadows/CSM.h b/Source/Falcor/Effects/Shadows/CSM.h similarity index 51% rename from Framework/Source/Effects/Shadows/CSM.h rename to Source/Falcor/Effects/Shadows/CSM.h index 35ee2ff5c..75d8486d1 100644 --- a/Framework/Source/Effects/Shadows/CSM.h +++ b/Source/Falcor/Effects/Shadows/CSM.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,22 +26,19 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/Texture.h" +#include "RenderGraph/RenderGraph.h" +#include "RenderGraph/RenderPass.h" #include "Data/Effects/CsmData.h" -#include "../Utils/GaussianBlur.h" -#include "Graphics/Light.h" -#include "Graphics/Scene/Scene.h" -#include "Utils/Math/ParallelReduction.h" -#include "Experimental/RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" +#include "Utils/Algorithm/ParallelReduction.h" namespace Falcor { - class Gui; class CsmSceneRenderer; /** Cascaded Shadow Maps Technique */ - class CascadedShadowMaps : public RenderPass, public inherit_shared_from_this + class dlldecl CascadedShadowMaps : public RenderPass { public: using SharedPtr = std::shared_ptr; @@ -54,144 +51,70 @@ namespace Falcor PSSM, }; - /** Destructor - */ - ~CascadedShadowMaps(); + static SharedPtr create(RenderContext* pRenderContext, const Dictionary& dict = {}); - /** Create a new instance. - \param[in] pLight Light to generate shadows for - \param[in] mapWidth Shadow map width - \param[in] mapHeight Shadow map height - \param[in] visibilityBufferWidth Visibility buffer width - \param[in] visibilityBufferHeight Visibility buffer height - \param[in] pScene Scene to render when generating shadow maps - \param[in] cascadeCount Number of cascades - \param[in] visMapBitsPerChannel Bits per channel of the visibility buffer - */ - static SharedPtr create(const Light::SharedConstPtr& pLight, uint32_t shadowMapWidth = 2048, uint32_t shadowMapHeight = 2048, uint32_t visibilityBufferWidth = 0, uint32_t visibilityBufferHeight = 0, const Scene::SharedPtr& pScene = nullptr, uint32_t cascadeCount = 4, uint32_t visMapBitsPerChannel = 16); - static SharedPtr create(const Dictionary& dict = {}); - - /** Render UI controls - \param[in] pGui GUI instance to render UI elements with - \param[in] uiGroup Optional name. If specified, UI elements will be rendered within a named group - */ - void renderUI(Gui* pGui, const char* uiGroup = "") override; - - /** Run the shadow-map generation pass and the visibility pass. Returns the visibility buffer - \params[in] pScene The scene to render - \params[in] pCamera The camera that will be used to render the scene - \params[in] pSceneDepthBuffer Valid only when SDSM is enabled. The depth map to run SDSM analysis on. If this is nullptr, SDSM will run a depth pass - */ - Texture::SharedPtr generateVisibilityBuffer(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pSceneDepthBuffer); + virtual std::string getDesc() override { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void renderUI(Gui::Widgets& widget) override; - /** Get the shadow map texture. - */ - Texture::SharedPtr getShadowMap() const; + void toggleMeshCulling(bool enabled); - /** Set number of cascade partitions. - */ + // Scripting functions void setCascadeCount(uint32_t cascadeCount); - - /** Get the number of partitions. - */ - uint32_t getCascadeCount() { return mCsmData.cascadeCount; } - - /** Set whether to use SDSM. - */ - void toggleMinMaxSdsm(bool enable) { mControls.useMinMaxSdsm = enable; } - - /** Set the min and max distance from the camera to generate shadows for. - */ - void setDistanceRange(const glm::vec2& range) { mControls.distanceRange = range; } - - /** Set the filter mode. Options are defined in CsmData.h - */ + void setMapWidth(uint32_t width); + void setMapHeight(uint32_t height); + void setVisibilityBufferBitsPerChannel(uint32_t bitsPerChannel); void setFilterMode(uint32_t filterMode); - - /** Get the filter mode. - */ - uint32_t getFilterMode() const { return mCsmData.filterMode; } - - /** Set the kernel width for PCF - */ + void setSdsmReadbackLatency(uint32_t latency); + void setPartitionMode(uint32_t partitionMode) { mControls.partitionMode = static_cast(partitionMode); } + void setPSSMLambda(float lambda) { mControls.pssmLambda = clamp(lambda, 0.f, 1.0f); } + void setMinDistanceRange(float min) { mControls.distanceRange.x = clamp(min, 0.f, 1.f); } + void setMaxDistanceRange(float max) { mControls.distanceRange.y = clamp(max, 0.f, 1.f); } + void setCascadeBlendThreshold(float threshold) { mCsmData.cascadeBlendThreshold = clamp(threshold, 0.f, 1.0f); } + void setDepthBias(float bias) { mCsmData.depthBias = max(0.f, bias); } void setPcfKernelWidth(uint32_t width) { mCsmData.pcfKernelWidth = width | 1; } - - /** Set the anistropy level for VSM/EVSM - */ void setVsmMaxAnisotropy(uint32_t maxAniso) { createVsmSampleState(maxAniso); } + void setVsmLightBleedReduction(float reduction) { mCsmData.lightBleedingReduction = clamp(reduction, 0.f, 1.0f); } + void setEvsmPositiveExponent(float exp) { mCsmData.evsmExponents.x = clamp(exp, 0.f, 5.54f); } + void setEvsmNegativeExponent(float exp) { mCsmData.evsmExponents.y = clamp(exp, 0.f, 5.54f); } + uint32_t getCascadeCount() { return mCsmData.cascadeCount; } + uint32_t getMapWidth() { return mMapWidth; } + uint32_t getMapHeight() { return mMapHeight; } + uint32_t getVisibilityBufferBitsPerChannel() { return mVisibilityPassData.mapBitsPerChannel; } + uint32_t getFilterMode() { return (uint32_t)mCsmData.filterMode; } + uint32_t getSdsmReadbackLatency() { return mSdsmData.readbackLatency; } + uint32_t getPartitionMode() { return (uint32_t)mControls.partitionMode; } + float getPSSMLambda() { return mControls.pssmLambda; } + float getMinDistanceRange() { return mControls.distanceRange.x; } + float getMaxDistanceRange() { return mControls.distanceRange.y; } + float getCascadeBlendThreshold() { return mCsmData.cascadeBlendThreshold; } + float getDepthBias() { return mCsmData.depthBias; } + uint32_t getPcfKernelWidth() { return mCsmData.pcfKernelWidth; } + uint32_t getVsmMaxAnisotropy() { return mShadowPass.pVSMTrilinearSampler->getMaxAnisotropy(); } + float getVsmLightBleedReduction() { return mCsmData.lightBleedingReduction; } + float getEvsmPositiveExponent() { return mCsmData.evsmExponents.x; } + float getEvsmNegativeExponent() { return mCsmData.evsmExponents.y; } - /** Set light-bleed reduction for VSM/EVSM - */ - void setVsmLightBleedReduction(float reduction) { mCsmData.lightBleedingReduction = reduction; } - - /** Set the depth bias - */ - void setDepthBias(float depthBias) { mCsmData.depthBias = depthBias; } - - /** Set the readback latency for SDSM (in frames) - */ - void setSdsmReadbackLatency(uint32_t latency); - - /** Set the width and sigma used when blurring the EVSM shadow-map - */ - void setEvsmBlur(uint32_t kernelWidth, float sigma); - - /** Enable mesh-culling for the shadow-map generation - */ - void toggleMeshCulling(bool enabled); - - /** Check if mesh-culling is enabled - */ - bool isMeshCullingEnabled() const; - - /** Enable saving cascade info into the gba channels of the visibility buffer - */ - void toggleCascadeVisualization(bool shouldVisualze); - - /** Set the visibility's buffer bits-per-channel - */ - void setVisibilityBufferBitsPerChannel(uint32_t bitsPerChannel); - - /** Reflect the render-pass - */ - virtual RenderPassReflection reflect() const override; - - /** Execute the render-pass - */ - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; - - /** Get a description of the pass - */ - virtual std::string getDesc() override { return kDesc; } - - /** Set the scene - */ - void setScene(const Scene::SharedPtr& pScene) override; - - /** Set the dimensions of the internal shadow-map - */ - void resizeShadowMap(uint32_t width, uint32_t height); - - /** Set the light-source - */ - void setLight(const Light::SharedConstPtr& pLight); - - /** Resize callback - */ - virtual void onResize(uint32_t width, uint32_t height) override; private: - CascadedShadowMaps(uint32_t mapWidth = 2048, uint32_t mapHeight = 2048); + CascadedShadowMaps(); + uint32_t mMapWidth = 2048; + uint32_t mMapHeight = 2048; Light::SharedConstPtr mpLight; Camera::SharedPtr mpLightCamera; - std::shared_ptr mpCsmSceneRenderer; - std::shared_ptr mpSceneRenderer; + //std::shared_ptr mpCsmSceneRenderer; + Scene::SharedPtr mpScene; - // Set shadow map generation parameters into a program. - void setDataIntoGraphicsVars(GraphicsVars::SharedPtr pVars, const std::string& varName); - vec2 calcDistanceRange(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pDepthBuffer); void createDepthPassResources(); - void createShadowPassResources(uint32_t mapWidth, uint32_t mapHeight); + void createShadowPassResources(); void createVisibilityPassResources(); + + // Set shadow map generation parameters into a program. + void setDataIntoVars(const GraphicsVars::SharedPtr& pVars, const std::string& varName); + vec2 calcDistanceRange(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pDepthBuffer); void partitionCascades(const Camera* pCamera, const glm::vec2& distanceRange); void renderScene(RenderContext* pCtx); @@ -223,7 +146,8 @@ namespace Falcor void reduceDepthSdsmMinMax(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr pDepthBuffer); void createVsmSampleState(uint32_t maxAnisotropy); - GaussianBlur::UniquePtr mpGaussianBlur; + RenderGraph::SharedPtr mpBlurGraph; + Dictionary mBlurDict; // Depth-pass struct @@ -236,9 +160,8 @@ namespace Falcor //Visibility pass struct { - FullScreenPass::UniquePtr pPass; - GraphicsState::SharedPtr pState; - GraphicsVars::SharedPtr pGraphicsVars; + FullScreenPass::SharedPtr pPass; + Fbo::SharedPtr pFbo; uint32_t mVisualizeCascadesOffset; } mVisibilityPass; @@ -252,7 +175,7 @@ namespace Falcor uint32_t mapBitsPerChannel = 32; } mVisibilityPassData; - struct Controls + struct { bool depthClamp = true; bool useMinMaxSdsm = true; @@ -260,14 +183,19 @@ namespace Falcor float pssmLambda = 0.5f; PartitionMode partitionMode = PartitionMode::Logarithmic; bool stabilizeCascades = false; - }; + } mControls; int32_t renderCascade = 0; - Controls mControls; CsmData mCsmData; + bool mCullMeshes = true; + /** Resize + */ + void onResize(uint32_t width, uint32_t height); void setupVisibilityPassFbo(const Texture::SharedPtr& pVisBuffer); ProgramReflection::BindLocation mPerLightCbLoc; - void executeInternal(RenderContext* pRenderCtx, const Camera* pCamera, const Texture::SharedPtr& pSceneDepthBuffer); + + void resizeShadowMap(uint32_t width, uint32_t height); + void setLight(const Light::SharedConstPtr& pLight); }; } diff --git a/Source/Falcor/Effects/SkyBox/SkyBox.cpp b/Source/Falcor/Effects/SkyBox/SkyBox.cpp new file mode 100644 index 000000000..35379f3bc --- /dev/null +++ b/Source/Falcor/Effects/SkyBox/SkyBox.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "SkyBox.h" +#include "glm/gtx/transform.hpp" + +namespace Falcor +{ + const char* SkyBox::kDesc = "Render an environment-map. The map can be provided by the user or taken from a scene"; + + namespace + { + const Gui::DropdownList kFilterList = + { + { (uint32_t)Sampler::Filter::Linear, "Linear" }, + { (uint32_t)Sampler::Filter::Point, "Point" }, + }; + + const std::string kTarget = "target"; + const std::string kDepth = "depth"; + + // Dictionary keys + const std::string kTexName = "texName"; + const std::string kLoadAsSrgb = "loadAsSrgb"; + const std::string kRenderStereo = "renderStereo"; + const std::string kFilter = "filter"; + } + + SkyBox::SkyBox() + { + mpCubeScene = Scene::create("Effects/cube.obj"); + if (mpCubeScene == nullptr) throw std::runtime_error("SkyBox::SkyBox - Failed to load cube model"); + + mpProgram = GraphicsProgram::createFromFile("Effects/SkyBox.slang", "vs", "ps"); + mpVars = GraphicsVars::create(mpProgram->getReflector()); + mpFbo = Fbo::create(); + + // Create state + mpState = GraphicsState::create(); + BlendState::Desc blendDesc; + for (uint32_t i = 1; i < Fbo::getMaxColorTargetCount(); i++) blendDesc.setRenderTargetWriteMask(i, false, false, false, false); + blendDesc.setIndependentBlend(true); + mpState->setBlendState(BlendState::create(blendDesc)); + + // Create the rasterizer state + RasterizerState::Desc rastDesc; + rastDesc.setCullMode(RasterizerState::CullMode::Front).setDepthClamp(true); + mpState->setRasterizerState(RasterizerState::create(rastDesc)); + + DepthStencilState::Desc dsDesc; + dsDesc.setDepthWriteMask(false).setDepthFunc(DepthStencilState::Func::LessEqual); + mpState->setDepthStencilState(DepthStencilState::create(dsDesc)); + mpState->setProgram(mpProgram); + + setFilter((uint32_t)mFilter); + } + + SkyBox::SharedPtr SkyBox::create(RenderContext* pRenderContext, const Dictionary& dict) + { + SharedPtr pSkyBox = SharedPtr(new SkyBox()); + for (const auto& v : dict) + { + if (v.key() == kTexName) + { + std::string name = v.val(); + pSkyBox->mTexName = name; + } + if (v.key() == kLoadAsSrgb) pSkyBox->mLoadSrgb = v.val(); + if (v.key() == kFilter) pSkyBox->setFilter((uint32_t)v.val()); + if (v.key() == kRenderStereo) pSkyBox->mRenderStereo = v.val(); + else logWarning("Unknown field '" + v.key() + "' in a SkyBox dictionary"); + } + + std::shared_ptr pTexture; + if (pSkyBox->mTexName.size() != 0) + { + pTexture = Texture::createFromFile(pSkyBox->mTexName, false, pSkyBox->mLoadSrgb); + if (pTexture == nullptr) throw std::runtime_error("SkyBox::create - Error creating texture from file"); + pSkyBox->setTexture(pTexture); + } + return pSkyBox; + } + + Dictionary SkyBox::getScriptingDictionary() + { + Dictionary dict; + dict[kTexName] = mTexName; + dict[kLoadAsSrgb] = mLoadSrgb; + dict[kFilter] = mFilter; + dict[kRenderStereo] = mRenderStereo; + return dict; + } + + RenderPassReflection SkyBox::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + reflector.addOutput(kTarget, "Color buffer").format(ResourceFormat::RGBA32Float); + auto& depthField = reflector.addInputOutput(kDepth, "Depth-buffer. Should be pre-initialized or cleared before calling the pass").bindFlags(Resource::BindFlags::DepthStencil); + return reflector; + } + + void SkyBox::compile(RenderContext* pContext, const CompileData& compileData) + { + // Create the program + Program::DefineList defines; + if (mRenderStereo) defines.add("_SINGLE_PASS_STEREO"); + mpProgram->addDefines(defines); + } + + void SkyBox::execute(RenderContext* pRenderContext, const RenderData& renderData) + { + if (!mpScene) return; + + mpFbo->attachColorTarget(renderData[kTarget]->asTexture(), 0); + mpFbo->attachDepthStencilTarget(renderData[kDepth]->asTexture()); + + pRenderContext->clearRtv(mpFbo->getRenderTargetView(0).get(), vec4(0)); + + glm::mat4 world = glm::translate(mpScene->getCamera()->getPosition()); + mpVars["PerFrameCB"]["gWorld"] = world; + mpVars["PerFrameCB"]["gScale"] = mScale; + mpVars["PerFrameCB"]["gViewMat"] = mpScene->getCamera()->getViewMatrix(); + mpVars["PerFrameCB"]["gProjMat"] = mpScene->getCamera()->getProjMatrix(); + mpState->setFbo(mpFbo); + mpCubeScene->render(pRenderContext, mpState.get(), mpVars.get(), Scene::RenderFlags::UserRasterizerState); + } + + void SkyBox::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) + { + mpScene = pScene; + + if (mpScene && mpScene->getEnvironmentMap()) setTexture(mpScene->getEnvironmentMap()); + if (mpScene) mpCubeScene->setCamera(mpScene->getCamera()); + } + + void SkyBox::renderUI(Gui::Widgets& widget) + { + float scale = mScale; + if (widget.var("Scale", scale, 0.f)) setScale(scale); + + if (widget.button("Load Image")) { loadImage(); } + + uint32_t filter = (uint32_t)mFilter; + if (widget.dropdown("Filter", kFilterList, filter)) setFilter(filter); + } + + void SkyBox::loadImage() + { + std::string filename; + FileDialogFilterVec filters = { {"bmp"}, {"jpg"}, {"dds"}, {"png"}, {"tiff"}, {"tif"}, {"tga"} }; + if (openFileDialog(filters, filename)) + { + mpTexture = Texture::createFromFile(filename, false, mLoadSrgb); + setTexture(mpTexture); + } + } + + void SkyBox::setTexture(const Texture::SharedPtr& pTexture) + { + mpTexture = pTexture; + if (mpTexture) + { + assert(mpTexture->getType() == Texture::Type::TextureCube || mpTexture->getType() == Texture::Type::Texture2D); + (mpTexture->getType() == Texture::Type::Texture2D) ? mpProgram->addDefine("_SPHERICAL_MAP") : mpProgram->removeDefine("_SPHERICAL_MAP"); + } + mpVars["gTexture"] = mpTexture; + } + + void SkyBox::setFilter(uint32_t filter) + { + mFilter = (Sampler::Filter)filter; + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(mFilter, mFilter, mFilter); + mpSampler = Sampler::create(samplerDesc); + mpVars["gSampler"] = mpSampler; + } + + SCRIPT_BINDING(SkyBox) + { + auto c = m.regClass(SkyBox); + c.func_("scale", &SkyBox::setScale); + c.func_("scale", &SkyBox::getScale); + c.func_("filter", &SkyBox::setFilter); + c.func_("filter", &SkyBox::getFilter); + } +} diff --git a/Source/Falcor/Effects/SkyBox/SkyBox.h b/Source/Falcor/Effects/SkyBox/SkyBox.h new file mode 100644 index 000000000..893db0d34 --- /dev/null +++ b/Source/Falcor/Effects/SkyBox/SkyBox.h @@ -0,0 +1,76 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramReflection.h" +#include "Core/Program/ProgramVars.h" + +namespace Falcor +{ + class dlldecl SkyBox : public RenderPass + { + public: + using SharedPtr = std::shared_ptr; + static const char* kDesc; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + std::string getDesc() override { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void renderUI(Gui::Widgets& widget) override; + + void setScale(float scale) { mScale = scale; } + void setFilter(uint32_t filter); + float getScale() { return mScale; } + uint32_t getFilter() { return (uint32_t)mFilter; } + + private: + SkyBox(); + void loadImage(); + void setTexture(const Texture::SharedPtr& pTexture); + + float mScale = 1; + bool mLoadSrgb = true; + bool mRenderStereo = false; + Sampler::Filter mFilter = Sampler::Filter::Linear; + Texture::SharedPtr mpTexture; + std::string mTexName; + + Scene::SharedPtr mpCubeScene; + GraphicsProgram::SharedPtr mpProgram; + GraphicsVars::SharedPtr mpVars; + GraphicsState::SharedPtr mpState; + Fbo::SharedPtr mpFbo; + Scene::SharedPtr mpScene; + Sampler::SharedPtr mpSampler; + }; +} diff --git a/Source/Falcor/Effects/TAA/TAAPass.cpp b/Source/Falcor/Effects/TAA/TAAPass.cpp new file mode 100644 index 000000000..140b3e6ea --- /dev/null +++ b/Source/Falcor/Effects/TAA/TAAPass.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "TAAPass.h" + +namespace Falcor +{ + const char* TemporalAAPass::kDesc = "Temporal Anti-Aliasing"; + + namespace + { + const std::string kMotionVec = "motionVecs"; + const std::string kColorIn = "colorIn"; + const std::string kColorOut = "colorOut"; + + const std::string kAlpha = "alpha"; + const std::string kColorBoxSigma = "colorBoxSigma"; + + const std::string kShaderFilename = "Effects/TAA.ps.slang"; + } + + TemporalAAPass::TemporalAAPass() + { + mpPass = FullScreenPass::create(kShaderFilename); + mpFbo = Fbo::create(); + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + mpLinearSampler = Sampler::create(samplerDesc); + } + + TemporalAAPass::SharedPtr TemporalAAPass::create(RenderContext* pRenderContext, const Dictionary& dict) + { + SharedPtr pTAA = SharedPtr(new TemporalAAPass()); + for (const auto& v : dict) + { + if (v.key() == kAlpha) pTAA->mControls.alpha = v.val(); + if (v.key() == kColorBoxSigma) pTAA->mControls.colorBoxSigma = v.val(); + else logWarning("Unknown field '" + v.key() + "' in a TemporalAA dictionary"); + } + return pTAA; + } + + Dictionary TemporalAAPass::getScriptingDictionary() + { + Dictionary dict; + dict[kAlpha] = mControls.alpha; + dict[kColorBoxSigma] = mControls.colorBoxSigma; + return dict; + } + + RenderPassReflection TemporalAAPass::reflect(const CompileData& compileData) + { + RenderPassReflection reflection; + reflection.addInput(kMotionVec, "Screen-space motion vectors"); + reflection.addInput(kColorIn, "Color-buffer of the current frame"); + reflection.addOutput(kColorOut, "Anti-aliased color buffer"); + return reflection; + } + + void TemporalAAPass::execute(RenderContext* pContext, const RenderData& renderData) + { + const auto& pColorIn = renderData[kColorIn]->asTexture(); + const auto& pColorOut = renderData[kColorOut]->asTexture(); + const auto& pMotionVec = renderData[kMotionVec]->asTexture(); + allocatePrevColor(pColorOut.get()); + mpFbo->attachColorTarget(pColorOut, 0); + + // Make sure the dimensions match + assert((pColorIn->getWidth() == mpPrevColor->getWidth()) && (pColorIn->getWidth() == pMotionVec->getWidth())); + assert((pColorIn->getHeight() == mpPrevColor->getHeight()) && (pColorIn->getHeight() == pMotionVec->getHeight())); + assert(pColorIn->getSampleCount() == 1 && mpPrevColor->getSampleCount() == 1 && pMotionVec->getSampleCount() == 1); + + mpPass["PerFrameCB"]["gAlpha"] = mControls.alpha; + mpPass["PerFrameCB"]["gColorBoxSigma"] = mControls.colorBoxSigma; + mpPass["gTexColor"] = pColorIn; + mpPass["gTexMotionVec"] = pMotionVec; + mpPass["gTexPrevColor"] = mpPrevColor; + mpPass["gSampler"] = mpLinearSampler; + + mpPass->execute(pContext, mpFbo); + pContext->blit(pColorOut->getSRV(), mpPrevColor->getRTV()); + } + + void TemporalAAPass::allocatePrevColor(const Texture* pColorOut) + { + bool allocate = mpPrevColor == nullptr; + allocate = allocate || (mpPrevColor->getWidth() != pColorOut->getWidth()); + allocate = allocate || (mpPrevColor->getHeight() != pColorOut->getHeight()); + allocate = allocate || (mpPrevColor->getDepth() != pColorOut->getDepth()); + allocate = allocate || (mpPrevColor->getFormat() != pColorOut->getFormat()); + assert(pColorOut->getSampleCount() == 1); + + if (allocate) mpPrevColor = Texture::create2D(pColorOut->getWidth(), pColorOut->getHeight(), pColorOut->getFormat(), 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); + } + + void TemporalAAPass::renderUI(Gui::Widgets& widget) + { + widget.var("Alpha", mControls.alpha, 0.f, 1.0f, 0.001f); + widget.var("Color-Box Sigma", mControls.colorBoxSigma, 0.f, 15.f, 0.001f); + } + + SCRIPT_BINDING(TemporalAAPass) + { + auto c = m.regClass(TemporalAAPass); + c.func_("alpha", &TemporalAAPass::setAlpha); + c.func_("alpha", &TemporalAAPass::getAlpha); + c.func_("sigma", &TemporalAAPass::setColorBoxSigma); + c.func_("sigma", &TemporalAAPass::getColorBoxSigma); + } +} diff --git a/Framework/Source/Experimental/Raytracing/RtModel.h b/Source/Falcor/Effects/TAA/TAAPass.h similarity index 56% rename from Framework/Source/Experimental/Raytracing/RtModel.h rename to Source/Falcor/Effects/TAA/TAAPass.h index 6db1e3381..96fab66e4 100644 --- a/Framework/Source/Experimental/Raytracing/RtModel.h +++ b/Source/Falcor/Effects/TAA/TAAPass.h @@ -26,38 +26,47 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Model/Model.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" namespace Falcor { - class RtModel : public Model, inherit_shared_from_this + /** Temporal AA class + */ + class dlldecl TemporalAAPass : public RenderPass { public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; + using SharedPtr = std::shared_ptr; + static const char* kDesc; - static RtModel::SharedPtr createFromFile(const char* filename, RtBuildFlags buildFlags = RtBuildFlags::None, Model::LoadFlags flags = Model::LoadFlags::None); - static RtModel::SharedPtr createFromModel(const Model& model, RtBuildFlags buildFlags = RtBuildFlags::None); - RtBuildFlags getBuildFlags() const { return mBuildFlags; } + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - struct BottomLevelData - { - uint32_t meshBaseIndex = 0; - uint32_t meshCount = 0; - bool isStatic = true; - Buffer::SharedPtr pBlas; - }; + std::string getDesc() override { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; - uint32_t getBottomLevelDataCount() const { return (uint32_t)mBottomLevelData.size(); } - const BottomLevelData& getBottomLevelData(uint32_t index) const { return mBottomLevelData[index]; } + void setAlpha(float alpha) { mControls.alpha = alpha; } + void setColorBoxSigma(float sigma) { mControls.colorBoxSigma = sigma; } + float getAlpha() { return mControls.alpha; } + float getColorBoxSigma() { return mControls.colorBoxSigma; } private: - RtModel(const Model& model, RtBuildFlags buildFlags); - bool update() override; // Override update() from Model, which updates vertices for skinned models - void buildAccelerationStructure(); + TemporalAAPass(); + void allocatePrevColor(const Texture* pColorOut); + + FullScreenPass::SharedPtr mpPass; + Fbo::SharedPtr mpFbo; + Sampler::SharedPtr mpLinearSampler; + + struct + { + float alpha = 0.1f; + float colorBoxSigma = 1.0f; + } mControls; - std::vector mBottomLevelData; - RtBuildFlags mBuildFlags; - void createBottomLevelData(); + Texture::SharedPtr mpPrevColor; }; } diff --git a/Source/Falcor/Effects/ToneMapping/ToneMappingPass.cpp b/Source/Falcor/Effects/ToneMapping/ToneMappingPass.cpp new file mode 100644 index 000000000..e07165d48 --- /dev/null +++ b/Source/Falcor/Effects/ToneMapping/ToneMappingPass.cpp @@ -0,0 +1,350 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ToneMappingPass.h" +#include "Utils/Color/ColorUtils.h" + +namespace Falcor +{ + const char* ToneMappingPass::kDesc = "Tone-map a color-buffer. The resulting buffer is always in the [0, 1] range. The pass supports auto-exposure and eye-adaptation"; + + namespace + { + const Gui::DropdownList kOperatorList = + { + { (uint32_t)ToneMappingPass::Operator::Clamp, "Clamp to LDR" }, + { (uint32_t)ToneMappingPass::Operator::Linear, "Linear" }, + { (uint32_t)ToneMappingPass::Operator::Reinhard, "Reinhard" }, + { (uint32_t)ToneMappingPass::Operator::ReinhardModified, "Modified Reinhard" }, + { (uint32_t)ToneMappingPass::Operator::HejiHableAlu, "Heji's approximation" }, + { (uint32_t)ToneMappingPass::Operator::HableUc2, "Uncharted 2" }, + { (uint32_t)ToneMappingPass::Operator::Aces, "ACES" }, + { (uint32_t)ToneMappingPass::Operator::Photo, "Photo" } + }; + + const std::string kSrc = "src"; + const std::string kDst = "dst"; + + const std::string kOperator = "operator"; + const std::string kExposureKey = "exposureKey"; + const std::string kWhiteMaxLuminance = "whiteMaxLuminance"; + const std::string kLuminanceLod = "luminanceLod"; + const std::string kWhiteScale = "whiteScale"; + const std::string kExposureValue = "exposureValue"; + const std::string kFilmSpeed = "filmSpeed"; + const std::string kWhitePoint = "whitePoint"; + const std::string kApplyAcesCurve = "applyAcesCurve"; + + const std::string kShaderFilename = "Effects/ToneMapping.ps.slang"; + } + + ToneMappingPass::ToneMappingPass(ToneMappingPass::Operator op) + { + createLuminancePass(); + createToneMapPass(op); + + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); + mpPointSampler = Sampler::create(samplerDesc); + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); + mpLinearSampler = Sampler::create(samplerDesc); + } + + void ToneMappingPass::createLuminancePass() + { + mpLuminancePass = FullScreenPass::create(kShaderFilename); + mpLuminancePass->addDefine("_LUMINANCE"); + } + + void ToneMappingPass::createToneMapPass(ToneMappingPass::Operator op) + { + mpToneMapPass = FullScreenPass::create(kShaderFilename); + + mOperator = op; + switch (op) + { + case Operator::Clamp: + mpToneMapPass->addDefine("_CLAMP"); + break; + case Operator::Linear: + mpToneMapPass->addDefine("_LINEAR"); + break; + case Operator::Reinhard: + mpToneMapPass->addDefine("_REINHARD"); + break; + case Operator::ReinhardModified: + mpToneMapPass->addDefine("_REINHARD_MOD"); + break; + case Operator::HejiHableAlu: + mpToneMapPass->addDefine("_HEJI_HABLE_ALU"); + break; + case Operator::HableUc2: + mpToneMapPass->addDefine("_HABLE_UC2"); + break; + case Operator::Aces: + mpToneMapPass->addDefine("_ACES"); + break; + case Operator::Photo: + mpToneMapPass->getProgram()->addDefine("_PHOTO"); + break; + default: + should_not_get_here(); + } + } + + ToneMappingPass::SharedPtr ToneMappingPass::create(RenderContext* pRenderContext, const Dictionary& dict) + { + ToneMappingPass* pTM = new ToneMappingPass(Operator::Aces); + + try + { + for (const auto& v : dict) + { + if (v.key() == kOperator) pTM->setOperator(v.val()); + else if (v.key() == kExposureKey) pTM->mToneMappingData.exposureKey = v.val(); + else if (v.key() == kWhiteMaxLuminance) pTM->mToneMappingData.whiteMaxLuminance = v.val(); + else if (v.key() == kLuminanceLod) pTM->mToneMappingData.luminanceLod = v.val(); + else if (v.key() == kWhiteScale) pTM->mToneMappingData.whiteScale = v.val(); + else if (v.key() == kExposureValue) pTM->mExposureValue = v.val(); + else if (v.key() == kFilmSpeed) pTM->mFilmSpeed = v.val(); + else if (v.key() == kWhitePoint) pTM->mWhitePoint = v.val(); + else if (v.key() == kApplyAcesCurve) pTM->mToneMappingData.applyAcesCurve = v.val(); + else logWarning("Unknown field `" + v.key() + "` in a ToneMapping dictionary"); + } + } + catch (std::exception& e) + { + logWarning(std::string("Unable to convert dictionary to expected type: ") + e.what()); + } + + // Prepare constant buffer. + pTM->calculateColorTransform(); + pTM->updateConstants(); + + return ToneMappingPass::SharedPtr(pTM); + } + + Dictionary ToneMappingPass::getScriptingDictionary() + { + Dictionary d; + d[kOperator] = mOperator; + d[kExposureKey] = mToneMappingData.exposureKey; + d[kWhiteMaxLuminance] = mToneMappingData.whiteMaxLuminance; + d[kLuminanceLod] = mToneMappingData.luminanceLod; + d[kWhiteScale] = mToneMappingData.whiteScale; + d[kExposureValue] = mExposureValue; + d[kFilmSpeed] = mFilmSpeed; + d[kWhitePoint] = mWhitePoint; + d[kApplyAcesCurve] = mToneMappingData.applyAcesCurve; + return d; + } + + RenderPassReflection ToneMappingPass::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + reflector.addInput(kSrc, "Source texture"); + reflector.addOutput(kDst, "Tone-mapped output texture"); + return reflector; + } + + void ToneMappingPass::execute(RenderContext* pRenderContext, const RenderData& renderData) + { + auto pSrc = renderData[kSrc]->asTexture(); + auto pDst = renderData[kDst]->asTexture(); + Fbo::SharedPtr pFbo = Fbo::create(); + pFbo->attachColorTarget(pDst, 0); + + createLuminanceFbo(pSrc); + + //Set shared vars + mpToneMapPass["gColorTex"] = pSrc; + mpLuminancePass["gColorTex"] = pSrc; + mpToneMapPass["gColorSampler"] = mpPointSampler; + mpLuminancePass["gColorSampler"] = mpLinearSampler; + + // Calculate luminance + if (mOperator != Operator::Photo) + { + mpLuminancePass->execute(pRenderContext, mpLuminanceFbo); + mpLuminanceFbo->getColorTexture(0)->generateMips(pRenderContext); + } + + //Set Tone map vars + if (mOperator != Operator::Clamp) + { + mpToneMapPass["PerImageCB"].setBlob(&mToneMappingData, 0, sizeof(mToneMappingData)); + mpToneMapPass["gLuminanceTexSampler"] = mpLinearSampler; + mpToneMapPass["gLuminanceTex"] = mpLuminanceFbo->getColorTexture(0); + } + + // Tone map + mpToneMapPass->execute(pRenderContext, pFbo); + } + + void ToneMappingPass::createLuminanceFbo(const Texture::SharedPtr& pSrc) + { + bool createFbo = mpLuminanceFbo == nullptr; + ResourceFormat srcFormat = pSrc->getFormat(); + uint32_t bytesPerChannel = getFormatBytesPerBlock(srcFormat) / getFormatChannelCount(srcFormat); + + // Find the required texture size and format + ResourceFormat luminanceFormat = (bytesPerChannel == 32) ? ResourceFormat::R32Float : ResourceFormat::R16Float; + uint32_t requiredHeight = getLowerPowerOf2(pSrc->getHeight()); + uint32_t requiredWidth = getLowerPowerOf2(pSrc->getWidth()); + + if(createFbo == false) + { + createFbo = (requiredWidth != mpLuminanceFbo->getWidth()) || + (requiredHeight != mpLuminanceFbo->getHeight()) || + (luminanceFormat != mpLuminanceFbo->getColorTexture(0)->getFormat()); + } + + if(createFbo) + { + Fbo::Desc desc; + desc.setColorTarget(0, luminanceFormat); + mpLuminanceFbo = Fbo::create2D(requiredWidth, requiredHeight, desc, 1, Fbo::kAttachEntireMipLevel); + } + } + + void ToneMappingPass::renderUI(Gui::Widgets& widget) + { + uint32_t opIndex = static_cast(mOperator); + if (widget.dropdown("Operator", kOperatorList, opIndex)) + { + mOperator = static_cast(opIndex); + createToneMapPass(mOperator); + } + + if (mOperator != Operator::Photo) + { + widget.var("Exposure Key", mToneMappingData.exposureKey, 0.0001f, 200.0f, 0.0001f); + widget.var("Luminance LOD", mToneMappingData.luminanceLod, 0.f, 16.f, 0.025f); + //Only give option to change these if the relevant operator is selected + if (mOperator == Operator::ReinhardModified) + { + widget.var("White Luminance", mToneMappingData.whiteMaxLuminance, 0.1f, FLT_MAX, 0.2f); + } + else if (mOperator == Operator::HableUc2) + { + widget.var("Linear White", mToneMappingData.whiteScale, 0.f, 100.f, 0.01f); + } + } + else + { + bool recomputeConstants = false; + + recomputeConstants |= widget.var("Exposure value (EV)", mExposureValue, -24.f, 24.f, 0.1f, false, "%.1f"); + recomputeConstants |= widget.var("Film speed (ISO)", mFilmSpeed, 1.f, 6400.f, 1.f, false, "%.1f"); + + // Note: Color temperatures < ~1905K are out-of-gamut in Rec.709. + if (widget.var("White point (K)", mWhitePoint, 1905.f, 25000.f, 5.f, false, "%.0f")) + { + calculateColorTransform(); + recomputeConstants = true; + } + + // Display color widget for the currently chosen white point. + // We normalize the color so that max(RGB) = 1 for display purposes. + glm::float3 w = mSourceWhite; + w = w / std::max(std::max(w.r, w.g), w.b); + widget.rgbColor("", w); + + widget.checkbox("Apply ACES curve", (bool&)mToneMappingData.applyAcesCurve); + + if (recomputeConstants) updateConstants(); + } + } + + void ToneMappingPass::calculateColorTransform() + { + // Calculate color transform for the current white point. + glm::float3x3 whiteBalance = calculateWhiteBalanceTransformRGB_Rec709(mWhitePoint); + mColorTransform = (glm::float3x4)whiteBalance; + + // Calculate source illuminant, i.e. the color that transforms to a pure white (1, 1, 1) output at the current color settings. + mSourceWhite = inverse(whiteBalance) * glm::float3(1, 1, 1); + } + + void ToneMappingPass::updateConstants() + { + // Calculate final transform. + mLinearScale = pow(2.f, -mExposureValue) * mFilmSpeed / 100.f; + mToneMappingData.finalTransform = mColorTransform * mLinearScale; // Note: linearScale is baked into the transform. + } + + void ToneMappingPass::setOperator(Operator op) + { + if (op != mOperator) createToneMapPass(op); + } + + void ToneMappingPass::setExposureValue(float exposureValue) + { + mExposureValue = clamp(exposureValue, -24.f, 24.f); + updateConstants(); + } + + void ToneMappingPass::setFilmSpeed(float filmSpeed) + { + mFilmSpeed = clamp(filmSpeed, 1.f, 6400.f); + updateConstants(); + } + + void ToneMappingPass::setWhitePoint(float whitePoint) + { + mWhitePoint = clamp(whitePoint, 1905.f, 25000.f); + calculateColorTransform(); + updateConstants(); + } + + SCRIPT_BINDING(ToneMappingPass) + { + auto c = m.regClass(ToneMappingPass); + c.func_("operator", &ToneMappingPass::setOperator); + c.func_("operator", &ToneMappingPass::getOperator); + c.func_("exposureKey", &ToneMappingPass::setExposureKey); + c.func_("exposureKey", &ToneMappingPass::getExposureKey); + c.func_("whiteMaxLuminance", &ToneMappingPass::setWhiteMaxLuminance); + c.func_("whiteMaxLuminance", &ToneMappingPass::getWhiteMaxLuminance); + c.func_("luminanceLod", &ToneMappingPass::setLuminanceLod); + c.func_("luminanceLod", &ToneMappingPass::getLuminanceLod); + c.func_("whiteScale", &ToneMappingPass::setWhiteScale); + c.func_("whiteScale", &ToneMappingPass::getWhiteScale); + c.func_("exposureValue", &ToneMappingPass::setExposureValue); + c.func_("exposureValue", &ToneMappingPass::getExposureValue); + c.func_("filmSpeed", &ToneMappingPass::setFilmSpeed); + c.func_("filmSpeed", &ToneMappingPass::getFilmSpeed); + c.func_("whitePoint", &ToneMappingPass::setWhitePoint); + c.func_("whitePoint", &ToneMappingPass::getWhitePoint); + + auto op = m.enum_("ToneMapOp"); + op.regEnumVal(ToneMappingPass::Operator::Clamp).regEnumVal(ToneMappingPass::Operator::Linear).regEnumVal(ToneMappingPass::Operator::Reinhard); + op.regEnumVal(ToneMappingPass::Operator::ReinhardModified).regEnumVal(ToneMappingPass::Operator::HejiHableAlu); + op.regEnumVal(ToneMappingPass::Operator::HableUc2).regEnumVal(ToneMappingPass::Operator::Aces).regEnumVal(ToneMappingPass::Operator::Photo); + } +} diff --git a/Source/Falcor/Effects/ToneMapping/ToneMappingPass.h b/Source/Falcor/Effects/ToneMapping/ToneMappingPass.h new file mode 100644 index 000000000..a0846fe23 --- /dev/null +++ b/Source/Falcor/Effects/ToneMapping/ToneMappingPass.h @@ -0,0 +1,134 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Data/Effects/ToneMappingData.h" + +namespace Falcor +{ + /** Tone-mapping effect + */ + class dlldecl ToneMappingPass : public RenderPass + { + public: + using SharedPtr = std::shared_ptr; + static const char* kDesc; + + /** The tone-mapping operator to use + */ + enum class Operator + { + Clamp, ///< Clamp to [0, 1]. Just like LDR + Linear, ///< Linear mapping + Reinhard, ///< Reinhard operator + ReinhardModified, ///< Reinhard operator with maximum white intensity + HejiHableAlu, ///< John Hable's ALU approximation of Jim Heji's filmic operator + HableUc2, ///< John Hable's filmic tone-mapping used in Uncharted 2 + Aces, ///< Aces Filmic Tone-Mapping + Photo, ///< Photographic tonemapper with manual controls + }; + + /** Create a new object + */ + static SharedPtr create(RenderContext* pRenderContext, const Dictionary& dict); + + std::string getDesc() override { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + + // Scripting functions + void setOperator(Operator op); + void setExposureKey(float exposureKey) { mToneMappingData.exposureKey = max(0.001f, exposureKey); } + void setWhiteMaxLuminance(float maxLuminance) { mToneMappingData.whiteMaxLuminance = maxLuminance; } + void setLuminanceLod(float lod) { mToneMappingData.luminanceLod = clamp(lod, 0.0f, 16.0f); } + void setWhiteScale(float whiteScale) { mToneMappingData.whiteScale = max(0.001f, whiteScale); } + void setExposureValue(float exposureValue); + void setFilmSpeed(float filmSpeed); + void setWhitePoint(float whitePoint); + Operator getOperator() const { return mOperator; } + float getExposureKey() const { return mToneMappingData.exposureKey; } + float getWhiteMaxLuminance() const { return mToneMappingData.whiteMaxLuminance; } + float getLuminanceLod() const { return mToneMappingData.luminanceLod; } + float getWhiteScale() const { return mToneMappingData.whiteScale; } + float getExposureValue() { return mExposureValue; } + float getFilmSpeed() { return mFilmSpeed; } + float getWhitePoint() { return mWhitePoint; } + + private: + ToneMappingPass(Operator op); + void createToneMapPass(Operator op); + void createLuminancePass(); + void createLuminanceFbo(const Texture::SharedPtr& pSrc); + + void calculateColorTransform(); + void updateConstants(); + + Operator mOperator; + FullScreenPass::SharedPtr mpToneMapPass; + FullScreenPass::SharedPtr mpLuminancePass; + Fbo::SharedPtr mpLuminanceFbo; + Sampler::SharedPtr mpPointSampler; + Sampler::SharedPtr mpLinearSampler; + + float mExposureValue = 0.0f; // Exposure value (EV). + float mFilmSpeed = 100.0f; // Film speed (ISO). + float mWhitePoint = 6500.0f; // White point (K). + + // Pre-computed fields based on above settings + float mLinearScale; // Precomputed linear exposure scaling. + float3 mSourceWhite; // Source illuminant in RGB (the white point to which the image is transformed to conform to). + + float3x4 mColorTransform; // Color balance transform in RGB space (we only use the 3x3 part). + + ToneMappingData mToneMappingData; + }; + +#define tonemap_op(a) case ToneMappingPass::Operator::a: return #a + inline std::string to_string(ToneMappingPass::Operator op) + { + switch (op) + { + tonemap_op(Clamp); + tonemap_op(Linear); + tonemap_op(Reinhard); + tonemap_op(ReinhardModified); + tonemap_op(HejiHableAlu); + tonemap_op(HableUc2); + tonemap_op(Aces); + tonemap_op(Photo); + default: + should_not_get_here(); + return ""; + } + } +#undef tonemap_op +} diff --git a/Source/Falcor/Effects/Utils/GaussianBlurPass.cpp b/Source/Falcor/Effects/Utils/GaussianBlurPass.cpp new file mode 100644 index 000000000..97c2afa55 --- /dev/null +++ b/Source/Falcor/Effects/Utils/GaussianBlurPass.cpp @@ -0,0 +1,230 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "GaussianBlurPass.h" +#include "Utils/UI/Gui.h" +#define _USE_MATH_DEFINES +#include +#include "Core/API/RenderContext.h" + +namespace Falcor +{ + const char* GaussianBlurPass::kDesc = "Gaussian Blur"; + + namespace + { + const std::string kSrc = "src"; + const std::string kDst = "dst"; + + const std::string kKernelWidth = "kernelWidth"; + const std::string kSigma = "sigma"; + const std::string kSrcWidth = "mapWidth"; + const std::string kSrcHeight = "mapHeight"; + + const std::string kShaderFilename("Effects/GaussianBlur.ps.slang"); + } + + GaussianBlurPass::GaussianBlurPass() + { + mpFbo = Fbo::create(); + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + mpSampler = Sampler::create(samplerDesc); + } + + GaussianBlurPass::SharedPtr GaussianBlurPass::create(RenderContext* pRenderContext, const Dictionary& dict) + { + SharedPtr pBlur = SharedPtr(new GaussianBlurPass); + for (const auto& v : dict) + { + if (v.key() == kKernelWidth) pBlur->mKernelWidth = v.val(); + if (v.key() == kSigma) pBlur->mSigma = v.val(); + else logWarning("Unknown field '" + v.key() + "' in a GaussianBlurPass dictionary"); + } + return pBlur; + } + + Dictionary GaussianBlurPass::getScriptingDictionary() + { + Dictionary dict; + dict[kKernelWidth] = mKernelWidth; + dict[kSigma] = mSigma; + return dict; + } + + RenderPassReflection GaussianBlurPass::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + mReady = false; + if (compileData.connectedResources.getFieldCount() > 0) + { + const RenderPassReflection::Field* edge = compileData.connectedResources.getField(kSrc); + RenderPassReflection::Field::Type srcType = edge->getType(); + ResourceFormat srcFormat = edge->getFormat(); + uint32_t srcWidth = edge->getWidth(); + uint32_t srcHeight = edge->getHeight(); + uint32_t srcDepth = edge->getDepth(); + uint32_t srcSampleCount = edge->getSampleCount(); + uint32_t srcMipCount = edge->getMipCount(); + uint32_t srcArraySize = edge->getArraySize(); + + auto formatField = [=](RenderPassReflection::Field& f) { + return f.format(srcFormat).resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); + }; + + formatField(reflector.addInput(kSrc, "input image to be blurred")); + formatField(reflector.addOutput(kDst, "output blurred image")); + mReady = true; + } + else + { + reflector.addInput(kSrc, "input image to be blurred"); + reflector.addOutput(kDst, "output blurred image"); + } + return reflector; + } + + void GaussianBlurPass::compile(RenderContext* pContext, const CompileData& compileData) + { + if (!mReady) throw std::runtime_error("GaussianBlurPass::compile - missing incoming reflection information"); + + uint32_t arraySize = compileData.connectedResources.getField(kSrc)->getArraySize(); + Program::DefineList defines; + defines.add("_KERNEL_WIDTH", std::to_string(mKernelWidth)); + if (arraySize > 1) defines.add("_USE_TEX2D_ARRAY"); + + uint32_t layerMask = (arraySize > 1) ? ((1 << arraySize) - 1) : 0; + defines.add("_HORIZONTAL_BLUR"); + mpHorizontalBlur = FullScreenPass::create(kShaderFilename, defines, layerMask); + defines.remove("_HORIZONTAL_BLUR"); + defines.add("_VERTICAL_BLUR"); + mpVerticalBlur = FullScreenPass::create(kShaderFilename, defines, layerMask); + + // Make the programs share the vars + mpVerticalBlur->setVars(mpHorizontalBlur->getVars()); + + updateKernel(); + } + + void GaussianBlurPass::execute(RenderContext* pRenderContext, const RenderData& renderData) + { + auto pSrc = renderData[kSrc]->asTexture(); + mpFbo->attachColorTarget(renderData[kDst]->asTexture(), 0); + createTmpFbo(pSrc.get()); + + // Horizontal pass + mpHorizontalBlur["gSampler"] = mpSampler; + mpHorizontalBlur["gSrcTex"] = pSrc; + mpHorizontalBlur->execute(pRenderContext, mpTmpFbo); + + // Vertical pass + mpVerticalBlur["gSrcTex"] = mpTmpFbo->getColorTexture(0); + mpVerticalBlur->execute(pRenderContext, mpFbo); + } + + void GaussianBlurPass::createTmpFbo(const Texture* pSrc) + { + bool createFbo = mpTmpFbo == nullptr; + ResourceFormat srcFormat = pSrc->getFormat(); + + if (createFbo == false) + { + createFbo = (pSrc->getWidth() != mpTmpFbo->getWidth()) || + (pSrc->getHeight() != mpTmpFbo->getHeight()) || + (srcFormat != mpTmpFbo->getColorTexture(0)->getFormat()) || + pSrc->getArraySize() != mpTmpFbo->getColorTexture(0)->getArraySize(); + } + + if (createFbo) + { + Fbo::Desc fboDesc; + fboDesc.setColorTarget(0, srcFormat); + mpTmpFbo = Fbo::create2D(pSrc->getWidth(), pSrc->getHeight(), fboDesc, pSrc->getArraySize()); + } + } + + void GaussianBlurPass::renderUI(Gui::Widgets& widget) + { + if (widget.var("Kernel Width", (int&)mKernelWidth, 1, 15, 2)) setKernelWidth(mKernelWidth); + if (widget.slider("Sigma", mSigma, 0.001f, mKernelWidth / 2.f)) setSigma(mSigma); + } + + void GaussianBlurPass::setKernelWidth(uint32_t kernelWidth) + { + mKernelWidth = kernelWidth | 1; // Make sure the kernel width is an odd number + mPassChangedCB(); + } + + void GaussianBlurPass::setSigma(float sigma) + { + mSigma = sigma; + mPassChangedCB(); + } + + float getCoefficient(float sigma, float kernelWidth, float x) + { + float sigmaSquared = sigma * sigma; + float p = -(x*x) / (2 * sigmaSquared); + float e = exp(p); + + float a = 2 * (float)M_PI * sigmaSquared; + return e / a; + } + + void GaussianBlurPass::updateKernel() + { + uint32_t center = mKernelWidth / 2; + float sum = 0; + std::vector weights(center + 1); + for (uint32_t i = 0; i <= center; i++) + { + weights[i] = getCoefficient(mSigma, (float)mKernelWidth, (float)i); + sum += (i == 0) ? weights[i] : 2 * weights[i]; + } + + TypedBuffer::SharedPtr pBuf = TypedBuffer::create(mKernelWidth, Resource::BindFlags::ShaderResource); + + for (uint32_t i = 0; i <= center; i++) + { + float w = weights[i] / sum; + pBuf[center + i] = w; + pBuf[center - i] = w; + } + + mpHorizontalBlur["weights"] = (TypedBufferBase::SharedPtr)pBuf; + } + + SCRIPT_BINDING(GaussianBlurPass) + { + auto c = m.regClass(GaussianBlurPass); + c.func_("kernelWidth", &GaussianBlurPass::setKernelWidth); + c.func_("kernelWidth", &GaussianBlurPass::getKernelWidth); + c.func_("sigma", &GaussianBlurPass::setSigma); + c.func_("sigma", &GaussianBlurPass::getSigma); + } +} diff --git a/Source/Falcor/Effects/Utils/GaussianBlurPass.h b/Source/Falcor/Effects/Utils/GaussianBlurPass.h new file mode 100644 index 000000000..702a77bc6 --- /dev/null +++ b/Source/Falcor/Effects/Utils/GaussianBlurPass.h @@ -0,0 +1,72 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/Program/ProgramVars.h" +#include "Core/API/FBO.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "RenderGraph/RenderPass.h" + +namespace Falcor +{ + /** Gaussian-blur technique + */ + class dlldecl GaussianBlurPass : public RenderPass + { + public: + using SharedPtr = std::shared_ptr; + static const char* kDesc; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + std::string getDesc() { return kDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + + void setKernelWidth(uint32_t kernelWidth); + void setSigma(float sigma); + uint32_t getKernelWidth() { return mKernelWidth; } + float getSigma() { return mSigma; } + + private: + GaussianBlurPass(); + uint32_t mKernelWidth = 5; + float mSigma = 2.0f; + bool mReady = false; + void createTmpFbo(const Texture* pSrc); + void updateKernel(); + + FullScreenPass::SharedPtr mpHorizontalBlur; + FullScreenPass::SharedPtr mpVerticalBlur; + Fbo::SharedPtr mpFbo; + Fbo::SharedPtr mpTmpFbo; + Sampler::SharedPtr mpSampler; + }; +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveIntegrator.ps.slang b/Source/Falcor/Experimental/Scene/Lights/EmissiveIntegrator.ps.slang new file mode 100644 index 000000000..d09fcd35b --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveIntegrator.ps.slang @@ -0,0 +1,146 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Raster program that integrates each textured emissive triangle. + The host is expected to set _VIEWPORT_DIMENSION and _NUM_MESH_LIGHTS. +*/ +import Experimental.Scene.Lights.LightCollection; + +// Setup NvApi. We need this for fp32 atomics. +#define NV_SHADER_EXTN_SLOT u63 +#define NV_SHADER_EXTN_REGISTER_SPACE space0 +#include "NVAPI/nvHLSLExtns.h" + +// Check that defines are set. +#ifndef _VIEWPORT_DIM +#error _VIEWPORT_DIM is not defined +#endif + +#if _NUM_MESH_LIGHTS == 0 +#error No mesh lights +#endif + +cbuffer CB +{ + LightCollection gLights; ///< The light sources we are integrating. +} + +RWByteAddressBuffer gTexelSum; ///< Sum over texels (RGB) + number of texels (A) in RGBA32Float format. Using raw buffer for fp32 atomics compatibility. +SamplerState gPointSampler; ///< Sampler for fetching individual texels without filtering. + +struct VsOut +{ + float2 uv : TexCoord; + nointerpolation uint lightIdx : LightIdx; + float4 posH : SV_Position; +}; + + +/** Vertex shader. + We place textured emissive triangles in texture space scaled so that we get + one pixel shader execution per texel. + Non-textured emissive triangles are culled. +*/ +VsOut vsMain(uint vtxIdx : SV_VertexID) +{ + // Fetch light index + const uint triIdx = vtxIdx / 3; + const uint id = gLights.getLightIdx(triIdx); + + VsOut vsOut; + vsOut.uv = float2(0, 0); + vsOut.posH = float4(0, 0, 0, 1); + vsOut.lightIdx = id; + + // Check if triangle is textured. + if (gLights.meshData[id].isTextured()) + { + // TODO: We could do these computations in a geometry shader once per triangle, + // instead of duplicated per-vertex. Which is faster? This code is not performance critical. + + // Get triangle's bounding box in texture coordinates. + float2 uv[3]; + for (uint j = 0; j < 3; j++) + { + uv[j] = gLights.getVtxTexCoord(triIdx + j); + } + const float2 uvMin = min(min(uv[0], uv[1]), uv[2]); + const float2 uvMax = max(max(uv[0], uv[1]), uv[2]); + + // Pass on texture coordinate unmodified for pixel to use. + vsOut.uv = gLights.getVtxTexCoord(vtxIdx); // Normalized (u,v) coordinate. + + // Query texture dimensions. + float width = 0, height = 0, mips = 0; + gLights.emissiveTextures[id].GetDimensions(0, width, height, mips); + + // Compute raster position in texture space. + // We do this by offsetting UV so that it's always positive, scaled by texture dimension. + float2 offset = floor(uvMin); + float2 texelPos = (vsOut.uv - offset) * float2(width, height); // Vertex pos in texels + + // TODO: Check if triangle is too large for viewport. + // We need to detect that and handle it with a fallback mechanism (print error for now). + + // Convert to 2DH normalized device coordinates. + float2 ndcPos = texelPos * (2.f / _VIEWPORT_DIM) - 1.f; + + vsOut.posH = float4(ndcPos, 0.f, 1.f); // Place triangle at w = 1. + } + + // If triangle isn't textured, all three vertices are at (0,0) => culled degenerate triangle. + return vsOut; +} + +/** Pixel shader. + This is executed once per texel that the emissive triangle covers. +*/ +void psMain(VsOut vsIn, uint triIdx : SV_PrimitiveID) +{ + // TODO: Use conservative rasterization and compute analytic coverage in pixel shader. + // With standard rasterization, we only get approximate integrals and it's possible to entire + // miss small triangles. This leads to bias unless we explicitly set a small non-zero probability elsewhere. + // We could alternatively use MSAA to reduce errors, but we still have the problem with missed primitives. + + // Fetch texel using nearest-neighbor sampling. + const float2 uv = vsIn.uv; // Interpolated texture coordinate. + const uint lightIdx = vsIn.lightIdx; + float3 color = gLights.emissiveTextures[lightIdx].SampleLevel(gPointSampler, uv, 0.f).rgb; // Sampler at mip 0 + + // Compute weighted color. TODO: For now assume full coverage (= 1.0 in weight). + float weight = 1.f; + color *= weight; + + // Atomically accumulate texel color and sample count. + // TODO: Coalesce atomics across warp before global atomic to increase perf. + uint address = triIdx * 16; + NvInterlockedAddFp32(gTexelSum, address + 0, color.r); // Sum colors in fp32 RGB + NvInterlockedAddFp32(gTexelSum, address + 4, color.g); + NvInterlockedAddFp32(gTexelSum, address + 8, color.b); + NvInterlockedAddFp32(gTexelSum, address + 12, weight); // Sum weights in fp32 +} diff --git a/Framework/Source/Utils/Platform/ProgressBar.cpp b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.cpp similarity index 74% rename from Framework/Source/Utils/Platform/ProgressBar.cpp rename to Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.cpp index 07c360899..3fad28d4b 100644 --- a/Framework/Source/Utils/Platform/ProgressBar.cpp +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,26 +25,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Utils/Platform/ProgressBar.h" +#include "stdafx.h" +#include "EmissiveLightSampler.h" namespace Falcor { - ProgressBar::SharedPtr ProgressBar::create(const MessageList& list, uint32_t delayInMs) + bool EmissiveLightSampler::prepareProgram(ProgramBase* pProgram) const { - SharedPtr pBar = SharedPtr(new ProgressBar()); - pBar->platformInit(list, delayInMs); - return pBar; + return pProgram->addDefine("_EMISSIVE_LIGHT_SAMPLER_TYPE", std::to_string((uint32_t)mType)); } - ProgressBar::SharedPtr ProgressBar::create(const char* pMsg, uint32_t delayInMs) + void EmissiveLightSampler::registerScriptBindings(ScriptBindings::Module& m) { - MessageList list; - if (pMsg) + if (!m.classExists()) { - list.push_back(pMsg); + auto e = m.enum_("EmissiveLightSamplerType"); + e.regEnumVal(EmissiveLightSamplerType::Uniform); } - return create(list, delayInMs); } } diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.h b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.h new file mode 100644 index 000000000..0d0d79dd4 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.h @@ -0,0 +1,111 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "EmissiveLightSamplerType.h" + +namespace Falcor +{ + /** Base class for emissive light sampler implementations. + + All light samplers follows the same interface to make them interchangeable. + If an unrecoverable error occurs, these functions may throw exceptions. + */ + class dlldecl EmissiveLightSampler : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + virtual ~EmissiveLightSampler() = default; + + /** Updates the sampler to the current frame. + \param[in] pRenderContext The render context. + \return True if the lighting in the scene has changed. + */ + virtual bool update(RenderContext* pRenderContext) = 0; + + /** Add compile-time specialization to program to use this light sampler. + This function must be called every frame before the sampler is bound. + Note that ProgramVars may need to be re-created after this call, check the return value. + \param[in] pProgram The Program to add compile-time specialization to. + \return True if the ProgramVars needs to be re-created. + */ + virtual bool prepareProgram(ProgramBase* pProgram) const; + + /** Bind the light sampler data to a program vars object. + The default implementation calls setIntoBlockCommon(). + Note that prepareProgram() must have been called before this function. + \param[in] pVars The program vars to set the data into. + \param[in] pCB The constant buffer to set the data into. + \param[in] varName The name of the data variable in the constant buffer. + \return True if successful, false otherwise. + */ + virtual bool setIntoProgramVars(ProgramVars* pVars, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const { return setIntoBlockCommon(pVars->getDefaultBlock(), pCB, varName); } + + /** Bind the light sampler data to a parameter block object. + The default implementation calls setIntoBlockCommon(). + Note that prepareProgram() must have been called before this function. + \param[in] pBlock The parameter block to set the data into. + \param[in] varName The name of the data variable in the parameter block. + \return True if successful, false otherwise. + */ + virtual bool setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) const { return setIntoBlockCommon(pBlock, pBlock->getDefaultConstantBuffer(), varName); } + + /** Render the GUI. + \return True if settings that affect the rendering have changed. + */ + virtual bool renderUI(Gui::Widgets& widget) = 0; + + /** Returns the number of active lights. + The caller can use this to determine if the sampler should be enabled for the current frame. + Note that the light count may change after every call to update(). + \return Number of currently active lights. + */ + virtual uint32_t getLightCount() const = 0; + + /** Returns the type of emissive light sampler. + \return The type of the derived class. + */ + EmissiveLightSamplerType getType() const { return mType; } + + static void registerScriptBindings(ScriptBindings::Module& m); + + protected: + EmissiveLightSampler(EmissiveLightSamplerType type) : mType(type) {} + + /** Bind the light sampler data to a given constant buffer in a parameter block. + Note that prepareProgram() must have been called before this function. + \param[in] pBlock The parameter block to set the data into (possibly the default parameter block). + \param[in] pCB The constant buffer in the parameter block to set the data into. + \param[in] varName The name of the data variable. + \return True if successful, false otherwise. + */ + virtual bool setIntoBlockCommon(const ParameterBlock::SharedPtr& pBlock, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const = 0; + + // Internal state + const EmissiveLightSamplerType mType; ///< Type of emissive sampler. See EmissiveLightSamplerType.h. + }; +} diff --git a/Samples/Core/ComputeShader/Data/compute.hlsl b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.slang similarity index 52% rename from Samples/Core/ComputeShader/Data/compute.hlsl rename to Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.slang index a99489dc7..0de225e67 100644 --- a/Samples/Core/ComputeShader/Data/compute.hlsl +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSampler.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,44 +25,40 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "EmissiveLightSamplerType.h" +__exported import Experimental.Scene.Lights.EmissiveLightSamplerInterface; -Texture2D gInput; -RWTexture2D gOutput; +/** The host sets the _EMISSIVE_LIGHT_SAMPLER_TYPE define to select sampler. -groupshared float4 colors[16][16]; -groupshared float4 pixelated; + This code typedefs the chosen type to the type 'EmissiveLightSampler'. + All emissive samplers adhere to the same interface, but note that the + size of the 'EmissiveLightSampler' type may vary depending on the type. +*/ -[numthreads(16, 16, 1)] -void main(uint3 groupId : SV_GroupID, uint3 groupThreadId : SV_GroupThreadId) -{ - // Calculate the start position of the block - uint3 resDim; - gOutput.GetDimensions(resDim.x, resDim.y); +#if defined(_EMISSIVE_LIGHT_SAMPLER_TYPE) && _EMISSIVE_LIGHT_SAMPLER_TYPE == EMISSIVE_LIGHT_SAMPLER_UNIFORM + import Experimental.Scene.Lights.EmissiveUniformSampler; + typedef EmissiveUniformSampler EmissiveLightSampler; - uint2 posStart = groupId.xy * 16; - uint2 crd = posStart + groupThreadId.xy; +#elif defined(_EMISSIVE_LIGHT_SAMPLER_TYPE) + // Compile-time error if _EMISSIVE_LIGHT_SAMPLER_TYPE is an invalid type. + #error _EMISSIVE_LIGHT_SAMPLER_TYPE is not set to a supported type. See EmissiveLightSamplerType.h. - // Fetch all of the data into the shared local memory - colors[groupThreadId.x][groupThreadId.y] = gInput[crd]; - -#ifdef _PIXELATE - GroupMemoryBarrierWithGroupSync(); - if(any(groupThreadId) == false) +#else + // If _EMISSIVE_LIGHT_SAMPLER_TYPE is not defined, declare a null sampler. + // This case happens if the user imports EmissiveLightSampler.slang but doesn't use it. + struct NullEmissiveSampler : IEmissiveLightSampler { - pixelated = 0; - for(int i = 0 ; i < 16 ; i++) + void sampleLight(const float3 posW, const float3 normalW, inout SampleGenerator sg, out TriangleLightSample ls) { - for(int j = 0 ; j < 16 ; j++) - { - pixelated += colors[i][j]; - } + ls = {}; + return; } - pixelated /= 16*16; - } - GroupMemoryBarrierWithGroupSync(); - gOutput[crd] = pixelated.bgra; -#else - gOutput[crd] = colors[groupThreadId.x][groupThreadId.y].bgra; + float evalPdf(float3 posW, float3 normalW, const TriangleHit hit) + { + return 0.f; + } + }; + typedef NullEmissiveSampler EmissiveLightSampler; + #endif -} \ No newline at end of file diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerHelpers.slang b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerHelpers.slang new file mode 100644 index 000000000..d7c221aaa --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerHelpers.slang @@ -0,0 +1,119 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + + /** Shared utility functions for emissive light sampler implementations. + + These functions rely on LightCollection, which is a container holding the + global list of all emissive triangles in the scene. + + TODO: It would've been nice if we could have these functions in the interface + struct (representing the "base" class that "derived" classes can use). + */ +#include "HostDeviceSharedMacros.h" +#include "Utils/Math/MathConstants.slang" + +import Shading; +import Utils.Math.MathHelpers; +import Experimental.Scene.Lights.LightCollection; +import Experimental.Scene.Lights.EmissiveLightSamplerInterface; + +/** Samples a single triangle uniformly and evaluates the probability density function. + + \param[in] lights The light collection. + \param[in] posW Shading point in world space. + \param[in] triangleIndex Triangle index of sampled triangle. + \param[in] u Uniform random number (2D). + \param[out] ls Light sample (ls.pdf = 0 indicates an invalid sample). +*/ +void sampleTriangle(const LightCollection lights, const float3 posW, uint triangleIndex, float2 u, out TriangleLightSample ls) +{ + ls = {}; +#if _NUM_MESH_LIGHTS == 0 + return; +#else + // Sample the triangle uniformly. + const float3 barycentrics = sample_triangle(u); + ls.posW = lights.getPosition(triangleIndex, barycentrics); + + // Compute light vector and squared distance. + float3 toLight = ls.posW - posW; // Unnormalized light vector + const float distSqr = max(FLT_MIN, dot(toLight, toLight)); // Clamp to avoid NaNs below + ls.distance = sqrt(distSqr); + ls.dir = toLight / ls.distance; // Note: toLight can be zero. + + // Get triangle normal and associated quantities. + ls.normalW = lights.getTriangleData(triangleIndex).normal; + float triangleArea = lights.getTriangleData(triangleIndex).area; + + // Reject sample if back-facing. + float cosTheta = dot(ls.normalW, -ls.dir); + if (cosTheta <= 0.f) return; // ls.pdf==0 (and ls.Le==0) makes the sample invalid. + + // Evaluate emitted radiance. + ls.Le = lights.getEmissive(triangleIndex, barycentrics); + + // Compute probability density with respect to solid angle from the shading point. + // The farther away the light is and the larger the angle it is at, the larger the pdf becomes. The probability goes to infinity in the limit. + // Note: Guard against div-by-zero here by clamping. + float denom = max(FLT_MIN, cosTheta * triangleArea); + ls.pdf = distSqr / denom; + + // TODO: We can simplify the expressions by using the unnormalized quantities for computing the pdf. + // TODO: Look at SASS to understand the tradeoffs and instruction count for different varieties. What return values do we really need? + //ls.pdf = -2.f * distSqr * ls.distance / (dot(N, L); // Optimized (except N would have to be properly flipped above, before normalW is computed). +#endif // !_NUM_MESH_LIGHTS +} + +/** Evaluates the PDF for a light sample given a hit point on an emissive triangle. + \param[in] posW Shading point in world space. + \param[in] hit Triangle hit data. + \return Probability density with respect to solid angle at the shading point. +*/ +float evalTrianglePdf(const float3 posW, const TriangleHit hit) +{ +#if _NUM_MESH_LIGHTS == 0 + return 0.f; +#else + // Compute light vector and squared distance. + float3 toLight = hit.posW - posW; // Unnormalized light vector + const float distSqr = dot(toLight, toLight); + if (distSqr <= FLT_MIN) return 0.f; // Avoid NaNs below + float3 L = toLight / sqrt(distSqr); + + // Cosine of angle between the light's normal and the light vector (flip L since it points towards the light). + float cosTheta = dot(hit.normalW, -L); + if (cosTheta <= 0.f) return 0.f; + + // Compute probability density with respect to solid angle from the shading point. + // The farther away the light is and the larger the angle it is at, the larger the pdf becomes. The probability goes to infinity in the limit. + // Note: Guard against div-by-zero here by clamping. + // TODO: Do we need the clamp here? distSqr is already clamped, so NaN should not be possible (but +inf is). + float denom = max(FLT_MIN, cosTheta * hit.triangleArea); + return distSqr / denom; +#endif // !_NUM_MESH_LIGHTS +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerInterface.slang b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerInterface.slang new file mode 100644 index 000000000..3aa67261e --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerInterface.slang @@ -0,0 +1,77 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +__exported import Utils.Sampling.SampleGenerator; + +/** Slang interface and structs used by emissive light samplers. +*/ + +/** Describes a light sample on an emissive triangle. + If pdf == 0.0 is returned, the sample in invalid and the other fields should not be used. +*/ +struct TriangleLightSample +{ + float3 posW; ///< Sampled point on the light source in world space. + float3 normalW; ///< Normal of the sampled point on the light source in world space. + float3 dir; ///< Normalized direction from the shading point to the sampled point on the light source in world space. + float distance; ///< Distance from the shading point to the sampled point. + float3 Le; ///< Emitted radiance. This is zero if the light is back-facing or sample is invalid. + float pdf; ///< Probability density with respect to solid angle from the shading point. The range is [0,inf] (inclusive), where pdf == 0.0 indicates an invalid sample. +}; + +/** Describes a light sample at a hit point on an emissive triangle. + This is used for PDF evaluation. +*/ +struct TriangleHit +{ + float3 posW; ///< Sampled point on the triangle in world space. + float3 normalW; ///< Face normal of the triangle in world space, flipped for back-facing hits. + float triangleArea; ///< Area of the triangle in world space. + uint meshInstanceID; ///< Mesh instance ID (= global hit ID). + uint primitiveIndex; ///< Primitive index in mesh. +}; + +/** Slang interface for emissive light sampler implementations. +*/ +interface IEmissiveLightSampler +{ + /** Draw a single light sample. + \param[in] posW Shading point in world space. + \param[in] normalW Normal at the shading point in world space. + \param[in,out] sg Sample generator. + \param[out] ls Light sample. If ls.pdf == 0.0 the sample is invalid and should not be used. + */ + void sampleLight(const float3 posW, const float3 normalW, inout SampleGenerator sg, out TriangleLightSample ls); + + /** Evaluate the PDF at a shading point given a hit point on an emissive triangle. + \param[in] posW Shading point in world space. + \param[in] normalW Normal at the shading point in world space. + \param[in] hit Triangle hit data. + \return Probability density with respect to solid angle at the shading point. + */ + float evalPdf(float3 posW, float3 normalW, const TriangleHit hit); +}; diff --git a/Samples/Core/StereoRendering/Data/StereoRendering.vs.hlsl b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerType.h similarity index 61% rename from Samples/Core/StereoRendering/Data/StereoRendering.vs.hlsl rename to Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerType.h index bf4743904..9393adecf 100644 --- a/Samples/Core/StereoRendering/Data/StereoRendering.vs.hlsl +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveLightSamplerType.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,50 +25,41 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import ShaderCommon; -__import DefaultVS; +#pragma once +#include "Data/HostDeviceData.h" -VertexOut main(VertexIn vIn) -{ - VertexOut vOut; - - // Filled out in geometry shader - vOut.posH = float4(0.0f, 0.0f, 0.0f, 0.0f); - vOut.prevPosH = float4(0.0f, 0.0f, 0.0f, 0.0f); - - float4x4 worldMat = getWorldMat(vIn); - float4 posW = mul(vIn.pos, worldMat); - vOut.posW = posW.xyz; - -#ifdef HAS_TEXCRD - vOut.texC = vIn.texC; -#else - vOut.texC = 0; -#endif - -#ifdef HAS_COLORS - vOut.colorV = vIn.color; -#else - vOut.colorV = 0; -#endif - -#ifdef HAS_NORMAL - vOut.normalW = mul(vIn.normal, getWorldInvTransposeMat(vIn)).xyz; -#else - vOut.normalW = 0; +/** This enum is shared between CPU/GPU. + It enumerates the different emissive light samplers that are available. +*/ +enum class EmissiveLightSamplerType +// TODO: Remove the ifdefs and the include when Slang supports enum type specifiers. +#ifdef HOST_CODE + : uint32_t #endif +{ + Uniform = 0, +}; -#ifdef HAS_BITANGENT - vOut.bitangentW = mul(vIn.bitangent, (float3x3)getWorldMat(vIn)).xyz; -#else - vOut.bitangentW = 0; -#endif +// For shader specialization in EmissiveLightSampler.slang we can't use the enums. +// TODO: Find a way to remove this workaround. +#define EMISSIVE_LIGHT_SAMPLER_UNIFORM 0 -#ifdef HAS_LIGHTMAP_UV - vOut.lightmapC = vIn.lightmapC; -#else - vOut.lightmapC = 0; +#ifdef HOST_CODE +static_assert((uint32_t)EmissiveLightSamplerType::Uniform == EMISSIVE_LIGHT_SAMPLER_UNIFORM); #endif - return vOut; +// Define to_string() for EmissiveLightSamplerType for use in serialization. +#ifdef HOST_CODE +#define str(a) case EmissiveLightSamplerType::a: return #a +inline std::string to_string(EmissiveLightSamplerType type) +{ + switch (type) + { + str(Uniform); + default: + should_not_get_here(); + return ""; + } } +#undef str +#endif diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.cpp b/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.cpp new file mode 100644 index 000000000..e2a6079a9 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "EmissiveUniformSampler.h" + +namespace Falcor +{ + EmissiveUniformSampler::SharedPtr EmissiveUniformSampler::create(RenderContext* pRenderContext, Scene::SharedPtr pScene, const EmissiveUniformSamplerOptions& options) + { + SharedPtr ptr = SharedPtr(new EmissiveUniformSampler()); + return ptr->init(pRenderContext, pScene, options) ? ptr : nullptr; + } + + bool EmissiveUniformSampler::update(RenderContext* pRenderContext) + { + PROFILE("EmissiveUniformSampler::update"); + + // Update the light collection. + assert(mpLights); + bool lightingChanged = mpLights->update(pRenderContext); + + return lightingChanged; + } + + bool EmissiveUniformSampler::prepareProgram(ProgramBase* pProgram) const + { + // Call the base class first. + bool varsChanged = EmissiveLightSampler::prepareProgram(pProgram); + + // Specialize the program for the light collection. + assert(mpLights); + varsChanged |= mpLights->prepareProgram(pProgram); + + return varsChanged; + } + + bool EmissiveUniformSampler::renderUI(Gui::Widgets& widget) + { + bool dirty = false; + + auto collectionGroup = Gui::Group(widget, "LightCollection"); + if (collectionGroup.open()) + { + assert(mpLights); + dirty = mpLights->renderUI(collectionGroup); + collectionGroup.release(); + } + + return dirty; + } + + bool EmissiveUniformSampler::init(RenderContext* pRenderContext, Scene::SharedPtr pScene, const EmissiveUniformSamplerOptions& options) + { + mOptions = options; + + // Create light collection for the scene. + mpLights = LightCollection::create(pRenderContext, pScene); + if (!mpLights) return false; + + return true; + } + + bool EmissiveUniformSampler::setIntoBlockCommon(const ParameterBlock::SharedPtr& pBlock, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const + { + assert(pBlock); + assert(pCB); + + // Check that the struct exists. + if (pCB->getVariableOffset(varName) == ConstantBuffer::kInvalidOffset) + { + logError("EmissiveUniformSampler::setIntoBlockCommon() - Variable " + varName + " does not exist"); + return false; + } + std::string prefix = varName + "."; + + // Ok. The struct exists. + // In the following we validate it has the correct fields and set the data. + + // Bind the lights first. + assert(mpLights); + if (!mpLights->setIntoBlockCommon(pBlock, pCB, prefix + "_lights")) + { + logError("EmissiveUniformSampler::setIntoBlockCommon() - Failed to bind lights"); + return false; + } + + return true; + } + + void EmissiveUniformSampler::registerScriptBindings(ScriptBindings::Module& m) + { + EmissiveLightSampler::registerScriptBindings(m); + + if (!m.classExists()) + { + auto options = m.regClass(EmissiveUniformSamplerOptions); +#define field(f_) rwField(#f_, &EmissiveUniformSamplerOptions::f_) + // TODO + //options.field(usePreintegration); +#undef field + } + } +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.h b/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.h new file mode 100644 index 000000000..064d2a49c --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.h @@ -0,0 +1,119 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "EmissiveLightSampler.h" +#include "LightCollection.h" + +namespace Falcor +{ + /** Emissive light sampler using uniform sampling of the lights. + + This class wraps a LightCollection object, which holds the set of lights to sample. + */ + class dlldecl EmissiveUniformSampler : public EmissiveLightSampler, inherit_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; + + /** EmissiveUniformSampler configuration. + Note if you change options, please update registerScriptBindings(). + */ + // TODO: Rename to shorter name when scoped struct names can be used with the scripting. + //struct Options : Falcor::ScriptBindings::enable_to_string + struct EmissiveUniformSamplerOptions : Falcor::ScriptBindings::enable_to_string + { + // TODO + }; + + virtual ~EmissiveUniformSampler() = default; + + /** Creates a EmissiveUniformSampler for a given scene. + \param[in] pRenderContext The render context. + \param[in] pScene The scene. + \param[in] options The options to override the default behavior. + */ + static SharedPtr create(RenderContext* pRenderContext, Scene::SharedPtr pScene, const EmissiveUniformSamplerOptions& options = EmissiveUniformSamplerOptions()); + + /** Updates the sampler to the current frame. + \param[in] pRenderContext The render context. + \return True if the lighting in the scene has changed. + */ + virtual bool update(RenderContext* pRenderContext) override; + + /** Add compile-time specialization to program to use this light sampler. + This function must be called every frame before the sampler is bound. + Note that ProgramVars may need to be re-created after this call, check the return value. + \param[in] pProgram The Program to add compile-time specialization to. + \return True if the ProgramVars needs to be re-created. + */ + virtual bool prepareProgram(ProgramBase* pProgram) const override; + + /** Render the GUI. + \return True if setting the refresh flag is needed, false otherwise. + */ + virtual bool renderUI(Gui::Widgets& widget) override; + + /** Returns the number of active lights. + The caller can use this to determine if light sampling should be enabled for the + current frame. Note that the number may change after each call to update(). + \return Number of currently active lights. + */ + virtual uint32_t getLightCount() const override { return mpLights ? mpLights->getActiveLightCount() : 0; } + + /** Returns the current configuration. + */ + const EmissiveUniformSamplerOptions& getOptions() const { return mOptions; } + + /** Returns the internal LightCollection object. + */ + virtual LightCollection::SharedConstPtr getLightCollection() const { return mpLights; } + + static void registerScriptBindings(ScriptBindings::Module& m); + + protected: + EmissiveUniformSampler() : EmissiveLightSampler(EmissiveLightSamplerType::Uniform) {} + + bool init(RenderContext* pRenderContext, Scene::SharedPtr pScene, const EmissiveUniformSamplerOptions& options); + + /** Bind the light sampler data to a given constant buffer in a parameter block. + Note that prepareProgram() must have been called before this function. + \param[in] pBlock The parameter block to set the data into (possibly the default parameter block). + \param[in] pCB The constant buffer in the parameter block to set the data into. + \param[in] varName The name of the data variable. + \return True if successful, false otherwise. + */ + virtual bool setIntoBlockCommon(const ParameterBlock::SharedPtr& pBlock, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const override; + + // Configuration + EmissiveUniformSamplerOptions mOptions; ///< Current configuration options. + + // Internal state + LightCollection::SharedPtr mpLights; ///< The collection of lights to sample. + }; +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.slang b/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.slang new file mode 100644 index 000000000..6fed1b708 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EmissiveUniformSampler.slang @@ -0,0 +1,97 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Utils/Math/MathConstants.slang" + +import Utils.Sampling.SampleGenerator; +import Experimental.Scene.Lights.LightCollection; +import Experimental.Scene.Lights.EmissiveLightSamplerHelpers; +import Experimental.Scene.Lights.EmissiveLightSamplerInterface; + +/** Emissive light sampler using uniform sampling of the emissive triangles. + + The sampler implements the IEmissiveLightSampler interface (see + EmissiveLightSamplerInterface.slang for usage information). + + The struct wraps a LightCollection that stores the pre-processed lights. + The program should instantiate the struct below. See EmissiveLightSampler.slang. +*/ +struct EmissiveUniformSampler : IEmissiveLightSampler +{ + LightCollection _lights; ///< The light sources. + + /** Draw a single light sample. + \param[in] posW Shading point in world space. + \param[in] normalW Normal at the shading point in world space. + \param[in,out] sg Sample generator. + \param[out] ls Light sample. If ls.pdf == 0.0 the sample is invalid and should not be used. + */ + void sampleLight(const float3 posW, const float3 normalW, inout SampleGenerator sg, out TriangleLightSample ls) + { + ls = {}; + #if _NUM_MESH_LIGHTS == 0 + return; + #else + // Randomly pick a triangle out of the global list with uniform probability. + float uLight = sampleNext1D(sg); + uint triangleCount = _lights.triangleCount; + uint triangleIndex = min((uint)(uLight * triangleCount), triangleCount - 1); // Safety precaution as the result of the multiplication may be rounded to triangleCount even if uLight < 1.0 when triangleCount is large. + float triangleSelectionPdf = 1.0f / ((float)triangleCount); + + // Sample the triangle uniformly. + float2 u = sampleNext2D(sg); + sampleTriangle(_lights, posW, triangleIndex, u, ls); + + // The final probability density is the product of the sampling probabilities. + ls.pdf *= triangleSelectionPdf; + + #endif // _NUM_MESH_LIGHTS + } + + /** Evaluate the PDF at a shading point given a hit point on an emissive triangle. + \param[in] posW Shading point in world space. + \param[in] normalW Normal at the shading point in world space. + \param[in] hit Triangle hit data. + \return Probability density with respect to solid angle at the shading point. + */ + float evalPdf(float3 posW, float3 normalW, const TriangleHit hit) + { + #if _NUM_MESH_LIGHTS == 0 + return 0.0f; + #else + // Lights are chosen uniformly so the selection probability is just one over the number of lights. + float triangleSelectionPdf = 1.0f / ((float)_lights.triangleCount); + + // Compute triangle sampling probability with respect to solid angle from the shading point. + float trianglePdf = evalTrianglePdf(posW, hit); + + // The final probability density is the product of the sampling probabilities. + return triangleSelectionPdf * trianglePdf; + + #endif // !_NUM_MESH_LIGHTS + } +}; diff --git a/Source/Falcor/Experimental/Scene/Lights/EnvProbe.cpp b/Source/Falcor/Experimental/Scene/Lights/EnvProbe.cpp new file mode 100644 index 000000000..f0dc9098d --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EnvProbe.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "EnvProbe.h" +#include "glm/gtc/integer.hpp" + +namespace Falcor +{ + namespace + { + const char kShaderFilenameSetup[] = "Experimental/Scene/Lights/EnvProbeSetup.cs.slang"; + + // The defaults are 512x512 @ 64spp in the resampling step. + const uint32_t kDefaultDimension = 512; + const uint32_t kDefaultSpp = 64; + + // Default variable name used by setIntoProgramVars(). + const char kDefaultCbVar[] = "gEnvProbe"; + } + + EnvProbe::SharedPtr EnvProbe::create(RenderContext* pRenderContext, const std::string& filename) + { + SharedPtr ptr = SharedPtr(new EnvProbe()); + return ptr->init(pRenderContext, filename) ? ptr : nullptr; + } + + bool EnvProbe::setIntoParameterBlock(ParameterBlock* pBlock, const char varName[]) const + { + // Get the implicit CB for the block. + // Note that the default parameter block doesn't have an implicit CB. Return if that happens. + ConstantBuffer* pCB = pBlock->getDefaultConstantBuffer().get(); + if (!pCB) return false; + + std::string prefix = std::string(varName).empty() ? "" : std::string(varName) + '.'; + return setIntoBlockCommon(pBlock, pCB, prefix); + } + + bool EnvProbe::setIntoConstantBuffer(ProgramVars* pVars, ConstantBuffer* pCB, const char varName[]) const + { + assert(pVars); + assert(pCB); + + // Check that the struct exists. + std::string cbVar = std::string(varName).empty() ? kDefaultCbVar : varName; + if (pCB->getVariableOffset(cbVar) == ConstantBuffer::kInvalidOffset) + { + logError("EnvProbe::setIntoProgramVars() - Variable " + cbVar + " not found in constant buffer"); + return false; + } + + return setIntoBlockCommon(pVars->getDefaultBlock().get(), pCB, cbVar + '.'); + } + + bool EnvProbe::init(RenderContext* pRenderContext, const std::string& filename) + { + // Create compute program for the setup phase. + mpSetupPass = ComputePass::create(kShaderFilenameSetup, "main"); + if (!mpSetupPass) return false; + + // Create sampler. + // The lat-long map wraps around horizontally, but not vertically. Set the sampler to only wrap in U. + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + samplerDesc.setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + mpEnvSampler = Sampler::create(samplerDesc); + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); + samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + mpImportanceSampler = Sampler::create(samplerDesc); + + // Load environment map from file. Set it to generate mips and use linear color. + mpEnvMap = Texture::createFromFile(filename, true, false); + if (!mpEnvMap) + { + logError("EnvProbe::init() - Failed to load texture " + filename); + return false; + } + + // Create hierarchical importance map for sampling. + if (!createImportanceMap(pRenderContext, kDefaultDimension, kDefaultSpp)) + { + logError("EnvProbe::init() - Failed to create importance map" + filename); + return false; + } + + return true; + } + + bool EnvProbe::createImportanceMap(RenderContext* pRenderContext, uint32_t dimension, uint32_t samples) + { + assert(isPowerOf2(dimension)); + assert(isPowerOf2(samples)); + + // We create log2(N)+1 mips from NxN...1x1 texels resolution. + uint32_t mips = glm::log2(dimension) + 1; + assert((1u << (mips - 1)) == dimension); + assert(mips > 1 && mips <= 12); // Shader constant limits max resolution, increase if needed. + + // Create importance map. We have to set the RTV flag to be able to use generateMips(). + mpImportanceMap = Texture::create2D(dimension, dimension, ResourceFormat::R32Float, 1, mips, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess); + assert(mpImportanceMap); + + mpSetupPass["gEnvMap"] = mpEnvMap; + mpSetupPass["gImportanceMap"] = mpImportanceMap; + mpSetupPass["gEnvSampler"] = mpEnvSampler; + + uint32_t samplesX = std::max(1u, (uint32_t)std::sqrt(samples)); + uint32_t samplesY = samples / samplesX; + assert(samples == samplesX * samplesY); + + mpSetupPass["CB"]["outputDim"] = glm::uvec2(dimension); + mpSetupPass["CB"]["outputDimInSamples"] = glm::uvec2(dimension * samplesX, dimension * samplesY); + mpSetupPass["CB"]["numSamples"] = glm::uvec2(samplesX, samplesY); + mpSetupPass["CB"]["invSamples"] = 1.f / (samplesX * samplesY); + + // Execute setup pass to compute the square importance map (base mip). + mpSetupPass->execute(pRenderContext, dimension, dimension); + + // Populate mip hierarchy. We rely on the default mip generation for this. + mpImportanceMap->generateMips(pRenderContext); + + return true; + } + + bool EnvProbe::setIntoBlockCommon(ParameterBlock* pBlock, ConstantBuffer* pCB, const std::string& varName) const + { + assert(pBlock); + assert(pCB); + + // Set variables. + if (!pCB->setVariable(varName + "importanceBaseMip", mpImportanceMap->getMipCount() - 1)) return false; // The base mip is 1x1 texels + if (!pCB->setVariable(varName + "importanceInvDim", glm::vec2(1.f / mpImportanceMap->getWidth(), 1.f / mpImportanceMap->getHeight()))) return false; + + // Bind resources. + if (!pBlock->setTexture(varName + "envMap", mpEnvMap) || + !pBlock->setTexture(varName + "importanceMap", mpImportanceMap) || + !pBlock->setSampler(varName + "envSampler", mpEnvSampler) || + !pBlock->setSampler(varName + "importanceSampler", mpImportanceSampler)) + { + return false; + } + + return true; + } +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EnvProbe.h b/Source/Falcor/Experimental/Scene/Lights/EnvProbe.h new file mode 100644 index 000000000..aa419545a --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EnvProbe.h @@ -0,0 +1,85 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +namespace Falcor +{ + class ParameterBlock; + + /** Environment map based radiance probe. + Utily class for sampling and evaluating radiance stored in an omnidirectional environment map. + */ + class dlldecl EnvProbe : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; + + virtual ~EnvProbe() = default; + + /** Create a new object + \param[in] pRenderContext A render-context that will be used for processing + \param[in] filename The env-map texture filename + */ + static SharedPtr create(RenderContext* pRenderContext, const std::string& filename); + + /** Binds environment map probe into a ParameterBlock. This is the recommended way of binding it. + \param[in] pBlock ParameterBlock to set data into. + \param[in] varName The name of the EnvProbe struct within the block, or empty if EnvProbe is the block. + \return false if there was an error, true otherwise. + */ + bool setIntoParameterBlock(ParameterBlock* pBlock, const char varName[]) const; + + /** Binds environment map probe into a constant buffer. + \param[in] pVars ProgramVars of the program to set data into. + \param[in] pCB The constant buffer to set the parameters into. + \param[in] varName The name of the EnvProbe member in the buffer (default 'gEnvProbe'). + \return false if there was an error, true otherwise. + */ + bool setIntoConstantBuffer(ProgramVars* pVars, ConstantBuffer* pCB, const char varName[] = "") const; + + const Texture::SharedPtr& getEnvMap() const { return mpEnvMap; } + const Texture::SharedPtr& getImportanceMap() const { return mpImportanceMap; } + const Sampler::SharedPtr& getEnvSampler() const { return mpEnvSampler; } + + protected: + EnvProbe() = default; + + bool init(RenderContext* pRenderContext, const std::string& filename); + bool createImportanceMap(RenderContext* pRenderContext, uint32_t dimension, uint32_t samples); + bool setIntoBlockCommon(ParameterBlock* pBlock, ConstantBuffer* pCB, const std::string& varName) const; + + ComputePass::SharedPtr mpSetupPass; ///< Compute pass for creating the importance map. + + Texture::SharedPtr mpEnvMap; ///< Loaded environment map (RGB). + Texture::SharedPtr mpImportanceMap; ///< Hierarchical importance map (luminance). + + Sampler::SharedPtr mpEnvSampler; + Sampler::SharedPtr mpImportanceSampler; + }; +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EnvProbe.slang b/Source/Falcor/Experimental/Scene/Lights/EnvProbe.slang new file mode 100644 index 000000000..bbab212d4 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EnvProbe.slang @@ -0,0 +1,157 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Utility functions for environment map sampling. + + Use the class EnvProbe on the host to load and prepare the env map. + The class builds an hierarchical importance map, which is used here + for importance sampling. +*/ + +#include "Utils/Math/MathConstants.slang" +import Utils.Math.MathHelpers; + +/** Struct describing an environment map light probe. +*/ +struct EnvProbe +{ + SamplerState envSampler; + SamplerState importanceSampler; ///< Point sampling with clamp to edge. + Texture2D envMap; ///< Environment map. + Texture2D importanceMap; ///< Hierarchical importance map (entire mip chain). + float2 importanceInvDim; ///< 1.0 / dimension. + uint importanceBaseMip; ///< Mip level for 1x1 resolution. + uint _pad; + // TODO: Add scalar value for total integrated intensity, i.e., same as 1x1 mip +}; + +/** Struct returned from the sampling functions. +*/ +struct EnvProbeSamplingResult +{ + float3 wi; ///< Sampled direction towards the light in world space. TODO: Rename dir. + float pdf; ///< Probability density function for the sampled direction with respect to solid angle. +}; + +/** Evaluates the radiance coming from world space direction 'dir'. +*/ +float3 evalEnvProbe(const EnvProbe probe, float3 dir, float lod = 0.f) +{ + // Get (u,v) coord in latitude-longitude map format. + float2 uv = world_to_latlong_map(dir); + return probe.envMap.SampleLevel(probe.envSampler, uv, lod).rgb; +} + +/** Evaluates the probability density function for a specific direction. + Note that the sampleEnvProbe() function already returns the pdf for the sampled location. + But, in some cases we need to evaluate the pdf for other directions (e.g. for MIS). + + \param[in] EnvProbe The light probe. + \param[in] dir World space direction (normalized). + \return Probability density function evaluated for direction 'dir'. +*/ +float evalEnvProbePdf(const EnvProbe probe, float3 dir) +{ + float2 uv = ndir_to_oct_equal_area_unorm(dir); + float avg_w = probe.importanceMap.Load(int3(0, 0, probe.importanceBaseMip)); // 1x1 mip holds integral over importance map. TODO: Replace by constant or rescale in setup so that the integral is 1.0 + float pdf = probe.importanceMap.SampleLevel(probe.importanceSampler, uv, 0) / avg_w; + return pdf * (1.f / M_4PI); +} + +/** Importance sampling of the env probe. +*/ +void sampleEnvProbe(const EnvProbe probe, const float2 rnd, inout EnvProbeSamplingResult result) +{ + float2 p = rnd; // Random sample in [0,1)^2. + uint2 pos = 0; // Top-left texel pos of current 2x2 region. + + // Iterate over mips of 2x2...NxN resolution. + for (int mip = probe.importanceBaseMip - 1; mip >= 0; mip--) + { + // Scale position to current mip. + pos *= 2; + + // Load the four texels at the current position. + float w[4]; + w[0] = probe.importanceMap.Load(int3(pos, mip)); + w[1] = probe.importanceMap.Load(int3(pos + uint2(1, 0), mip)); + w[2] = probe.importanceMap.Load(int3(pos + uint2(0, 1), mip)); + w[3] = probe.importanceMap.Load(int3(pos + uint2(1, 1), mip)); + + float q[2]; + q[0] = w[0] + w[2]; + q[1] = w[1] + w[3]; + + uint2 off; + + // Horizontal warp. + float d = q[0] / (q[0] + q[1]); // TODO: Do we need to guard against div-by-zero. We should ensure we never go down a path that has p=0. + + if (p.x < d) // left + { + off.x = 0; + p.x = p.x / d; + } + else // right + { + off.x = 1; + p.x = (p.x - d) / (1.f - d); + } + + // Vertical warp. + float e = w[off.x] / q[off.x]; + + if (p.y < e) // bottom + { + off.y = 0; + p.y = p.y / e; + } + else // top + { + off.y = 1; + p.y = (p.y - e) / (1.f - e); + } + + pos += off; + } + + // At this point, we have chosen a texel 'pos' in the range [0,dimension) for each component. + // The 2D sample point 'p' has been warped along the way, and is in the range [0,1) representing sub-texel location. + + // Compute final sample position and map to direction. + float2 uv = ((float2)pos + p) * probe.importanceInvDim; // Final sample in [0,1)^2. + float3 dir = oct_to_ndir_equal_area_unorm(uv); + + // Compute final pdf. + // We sample exactly according to the intensity of where the final samples lies in the octahedral map, normalized to its average intensity. + float avg_w = probe.importanceMap.Load(int3(0, 0, probe.importanceBaseMip)); // 1x1 mip holds integral over importance map. TODO: Replace by constant or rescale in setup so that the integral is 1.0 + float pdf = probe.importanceMap[pos] / avg_w; + + result.wi = dir; + result.pdf = pdf * M_1_4PI; +} diff --git a/Source/Falcor/Experimental/Scene/Lights/EnvProbeSetup.cs.slang b/Source/Falcor/Experimental/Scene/Lights/EnvProbeSetup.cs.slang new file mode 100644 index 000000000..aa8df3b2b --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/EnvProbeSetup.cs.slang @@ -0,0 +1,75 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Compute shader for building a hierarchical importance map from an + environment map. The result is used by EnvProbe.slang for sampling. +*/ + +import HostDeviceData; +import Utils.Math.MathHelpers; + +cbuffer CB +{ + uint2 outputDim; // Resolution of the importance map in texels. + uint2 outputDimInSamples; // Resolution of the importance map in samples. + uint2 numSamples; // Per-texel subsamples s.xy at finest mip. + float invSamples; // 1 / (s.x*s.y). +}; + +SamplerState gEnvSampler; +Texture2D gEnvMap; +RWTexture2D gImportanceMap; + +[numthreads(16, 16, 1)] +void main(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint2 pixel = dispatchThreadID.xy; + if (any(pixel >= outputDim)) return; + + float L = 0.f; + for (uint y = 0; y < numSamples.y; y++) + { + for (uint x = 0; x < numSamples.x; x++) + { + // Compute sample pos p in [0,1)^2 in octahedral map. + uint2 samplePos = pixel * numSamples + uint2(x, y); + float2 p = ((float2)samplePos + 0.5f) / outputDimInSamples; + + // Convert p to (u,v) coordinate in latitude-longitude map. + float3 dir = oct_to_ndir_equal_area_unorm(p); + float2 uv = world_to_latlong_map(dir); + + // Accumulate the radiance from this sample. + float3 radiance = gEnvMap.SampleLevel(gEnvSampler, uv, 0).rgb; + L += luminance(radiance); + } + } + + // Store average radiance for this texel. + gImportanceMap[pixel] = L * invSamples; +} diff --git a/Source/Falcor/Experimental/Scene/Lights/LightCollection.cpp b/Source/Falcor/Experimental/Scene/Lights/LightCollection.cpp new file mode 100644 index 000000000..db5b1afe2 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/LightCollection.cpp @@ -0,0 +1,652 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "LightCollection.h" +#include "LightCollectionShared.h" +#include + +namespace Falcor +{ + namespace + { + const char kFileEmissiveIntegrator[] = "Experimental/Scene/Lights/EmissiveIntegrator.ps.slang"; + const char kFileSetup[] = "Experimental/Scene/Lights/LightCollection.cs.slang"; + } + + LightCollection::SharedPtr LightCollection::create(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) + { + SharedPtr ptr = SharedPtr(new LightCollection()); + return ptr->init(pRenderContext, pScene) ? ptr : nullptr; + } + + bool LightCollection::update(RenderContext* pRenderContext, UpdateStatus* pUpdateStatus) + { + PROFILE("LightCollection::update()"); + + if (pUpdateStatus) + { + pUpdateStatus->lightsUpdateInfo.clear(); + pUpdateStatus->lightsUpdateInfo.reserve(mMeshLights.size()); + } + + // Update transform matrices and check for updates. + // TODO: Move per-mesh instance update flags into Scene. Return just a list of mesh lights that have changed. + std::vector updatedLights; + updatedLights.reserve(mMeshLights.size()); + + for (uint32_t lightIdx = 0; lightIdx < mMeshLights.size(); ++lightIdx) + { + const MeshInstanceData& instanceData = mpScene->getMeshInstance(mMeshLights[lightIdx].meshInstanceID); + UpdateFlags updateFlags = UpdateFlags::None; + + // Check if mesh has an active animation. + // This check is over-conservative. We should inspect all matrices referenced by the bones to detect skinning changes. + // TODO: Implement this check inside AnimationController and store extra flag to differentiate between rigid body and skinning changes. + if (mpScene->getAnimationController()->getMeshAnimationCount(instanceData.meshID) > 0) + { + bool hasAnimation = mpScene->getAnimationController()->getActiveAnimation(instanceData.meshID) != AnimationController::kBindPoseAnimationId; + bool isPaused = gpFramework->getGlobalClock().isPaused(); + if (hasAnimation && !isPaused) updateFlags |= UpdateFlags::AnimationChanged; + } + + // Check if instance transform changed. + if (mpScene->getAnimationController()->didMatrixChanged(instanceData.globalMatrixID)) updateFlags |= UpdateFlags::MatrixChanged; + + // Store update status. + if (updateFlags != UpdateFlags::None) updatedLights.push_back(lightIdx); + if (pUpdateStatus) pUpdateStatus->lightsUpdateInfo.push_back(updateFlags); + } + + // Update light data if needed. + if (!updatedLights.empty()) + { + updateTrianglePositions(pRenderContext, updatedLights); + return true; + } + + return false; + } + + bool LightCollection::prepareProgram(ProgramBase* pProgram) const + { + return pProgram->addDefine("_NUM_MESH_LIGHTS", std::to_string(mMeshLights.size())); + } + + bool LightCollection::renderUI(Gui::Widgets& widget) + { + // Prints stats about the number of lights etc. + const MeshLightStats& stats = getStats(); + std::ostringstream oss; + oss << "Mesh lights (input)" << std::endl + << " Meshes (total) : " << stats.meshLightCount << std::endl + << " Meshes (textured) : " << stats.meshesTextured << std::endl + << " Triangles (total) : " << stats.triangleCount << std::endl + << " Triangles (textured) : " << stats.trianglesTextured << std::endl + << std::endl + << "Mesh lights (pre-processed)" << std::endl + << " Triangles (culled) : " << stats.trianglesCulled << std::endl + << " Triangles (active) : " << stats.trianglesActive << std::endl + << " -> uniform emissive : " << stats.trianglesActiveUniform << std::endl + << " -> textured emissive : " << stats.trianglesActiveTextured << std::endl; + + widget.text(oss.str().c_str()); + + return false; + } + + bool LightCollection::init(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) + { + assert(pScene); + mpScene = pScene; + + // Setup the lights. + if (!setupMeshLights()) return false; + + // Create program for integrating emissive textures. + // This should be done after lights are setup, so that we know which sampler state etc. to use. + if (!initIntegrator()) return false; + + // Create programs for building/updating the mesh lights. + Shader::DefineList defines = mpScene->getSceneDefines(); + mpTriangleListBuilder = ComputePass::create(kFileSetup, "buildTriangleList", defines); + if (!mpTriangleListBuilder) return false; + mpTrianglePositionUpdater = ComputePass::create(kFileSetup, "updateTriangleVertices", defines); + if (!mpTrianglePositionUpdater) return false; + mpFinalizeIntegration = ComputePass::create(kFileSetup, "finalizeIntegration", Program::DefineList(), false); + if (!mpFinalizeIntegration) return false; + + mpStagingFence = GpuFence::create(); + if (!mpStagingFence) return false; + + // Now build the mesh light data. + build(pRenderContext); + + return true; + } + + bool LightCollection::initIntegrator() + { + // The current algorithm rasterizes emissive triangles in texture space, + // and uses atomic operations to sum up the contribution from all covered texels. + // We do this in a raster pass, so we get one thread per texel/triangle. + // TODO: Make this deterministic with regards to floating-point errors in the integration. + + std::string s; + if (findFileInDataDirectories("NVAPI/nvHLSLExtns.h", s) == false) + { + logError("LightCollection relies on NVAPI, which appears to be missing. Please make sure you have NVAPI installed (instructions are in the readme file)"); + return false; + } + + // Create program. + Program::Desc desc; + desc.addShaderLibrary(kFileEmissiveIntegrator).vsEntry("vsMain").psEntry("psMain"); + mIntegrator.pProgram = GraphicsProgram::create(desc); + if (!mIntegrator.pProgram) return false; + + // Create graphics state. + mIntegrator.pState = GraphicsState::create(); + + // Set state. + mIntegrator.pState->setProgram(mIntegrator.pProgram); + mIntegrator.pState->setVao(Vao::create(Vao::Topology::TriangleList)); + + // Set viewport. Note we don't bind any render targets so the size just determines the dispatch limits. + const uint32_t vpDim = 16384; // 16K x 16K + mIntegrator.pState->setViewport(0, GraphicsState::Viewport(0.f, 0.f, (float)vpDim, (float)vpDim, 0.f, 1.f)); + mIntegrator.pProgram->addDefine("_VIEWPORT_DIM", std::to_string(vpDim)); // Pass size to shader + + // Set raster state to disable culling. We don't care about winding in texture space when integrating. + RasterizerState::Desc rsDesc; + rsDesc.setCullMode(RasterizerState::CullMode::None); + rsDesc.setFillMode(RasterizerState::FillMode::Solid); + mIntegrator.pState->setRasterizerState(RasterizerState::create(rsDesc)); + + // Set depth-stencil state to disable depth test/writes. + DepthStencilState::Desc dsDesc; + dsDesc.setDepthEnabled(false); + dsDesc.setDepthWriteMask(false); + mIntegrator.pState->setDepthStencilState(DepthStencilState::create(dsDesc)); + + // Create sampler for texel fetch. This is identical to material sampler but uses point sampling. + Sampler::Desc samplerDesc = mpSamplerState ? mpSamplerState->getDesc() : Sampler::Desc(); + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); + mIntegrator.pPointSampler = Sampler::create(samplerDesc); + + return true; + } + + bool LightCollection::setupMeshLights() + { + mMeshLights.clear(); + mpSamplerState = nullptr; + mTriangleCount = 0; + + // Create mesh lights for all emissive mesh instances. + for (uint32_t meshInstanceID = 0; meshInstanceID < mpScene->getMeshInstanceCount(); meshInstanceID++) + { + const MeshInstanceData& instanceData = mpScene->getMeshInstance(meshInstanceID); + const MeshDesc& meshData = mpScene->getMesh(instanceData.meshID); + const Material::SharedPtr& pMaterial = mpScene->getMaterial(instanceData.materialID); + assert(pMaterial); + + if (pMaterial->isEmissive()) + { + // We've found a mesh instance with an emissive material => Setup mesh light data. + MeshLightData meshLight; + meshLight.meshInstanceID = meshInstanceID; + meshLight.triangleCount = meshData.indexCount / 3; + meshLight.triangleOffset = mTriangleCount; + meshLight.flags = PACK_EMISSIVE_TYPE(meshLight.flags, pMaterial->getEmissiveTexture() != nullptr ? ChannelTypeTexture : ChannelTypeConst); + meshLight.emissiveColor = pMaterial->getEmissiveColor(); + meshLight.emissiveFactor = pMaterial->getEmissiveFactor(); + + mMeshLights.push_back(meshLight); + mTriangleCount += meshLight.triangleCount; + + // Store ptr to texture sampler. We currently assume all the mesh lights' materials have the same sampler, which is true in current Falcor. + // If this changes in the future, we'll have to support multiple samplers. + if (pMaterial->getEmissiveTexture()) + { + if (!mpSamplerState) + { + mpSamplerState = pMaterial->getSampler(); + } + else if (mpSamplerState != pMaterial->getSampler()) + { + logError("Material '" + pMaterial->getName() + "' is using a different sampler."); + return false; + } + } + } + } + + return true; + } + + void LightCollection::build(RenderContext* pRenderContext) + { + if (mTriangleCount == 0) + { + // If there are no emissive triangle, clear everything and mark the CPU data/stats as valid. + mMeshLightTriangles.clear(); + mMeshLightStats = MeshLightStats(); + + mCPUInvalidData = CPUOutOfDateFlags::None; + mStagingBufferValid = true; + mStatsValid = true; + } + else + { + // Prepare GPU buffers. + prepareTriangleData(pRenderContext); + prepareMeshData(); + + // Pre-integrate emissive triangles. + // TODO: We might want to redo this in update() for animated meshes or after scale changes as that affects the flux. + integrateEmissive(pRenderContext); + + mCPUInvalidData = CPUOutOfDateFlags::All; + mStagingBufferValid = false; + mStatsValid = false; + + prepareSyncCPUData(pRenderContext); + } + } + + void LightCollection::prepareTriangleData(RenderContext* pRenderContext) + { + // Create GPU buffers. + assert(mTriangleCount > 0); + const size_t bufSize = mTriangleCount * 3 * sizeof(glm::vec3); + const size_t uvBufSize = mTriangleCount * 3 * sizeof(glm::vec2); + + mpMeshLightsVertexPos = Buffer::create(bufSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpMeshLightsVertexPos->setName("LightCollection_MeshLightsVertexPos"); + mpMeshLightsTexCoords = Buffer::create(uvBufSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpMeshLightsTexCoords->setName("LightCollection_MeshLightsTexCoords"); + mpTriangleData = StructuredBuffer::create(mpTriangleListBuilder->getProgram().get(), "gTriangleData", mTriangleCount); + mpTriangleData->setName("LightCollection_TriangleData"); + + // Compute triangle data (vertices, uv-coordinates, materialID) for all mesh lights. + buildTriangleList(pRenderContext); + } + + void LightCollection::prepareMeshData() + { + assert(mMeshLights.size() > 0); + + // Create buffer for the mesh data. + mpMeshData = StructuredBuffer::create(mpTriangleListBuilder->getProgram().get(), "gLights.meshData", mMeshLights.size(), ResourceBindFlags::ShaderResource); + if (!mpMeshData || mpMeshData->getElementSize() != sizeof(MeshLightData)) + { + throw std::exception("Failed to create structured buffer of MeshLightData"); + } + size_t meshDataSize = mMeshLights.size() * sizeof(mMeshLights[0]); + assert(mpMeshData && mpMeshData->getSize() == meshDataSize); + mpMeshData->setBlob(mMeshLights.data(), 0, meshDataSize); + + // Build a lookup table from mesh instance ID to emissive triangle offset. + // This is useful in ray tracing for locating the emissive triangle that was hit for MIS computation etc. + uint32_t instanceCount = mpScene->getMeshInstanceCount(); + assert(instanceCount > 0); + std::vector triangleOffsets(instanceCount, kInvalidIndex); + for (const auto& it : mMeshLights) + { + assert(it.meshInstanceID < instanceCount); + triangleOffsets[it.meshInstanceID] = it.triangleOffset; + } + + // Create the GPU buffer. + mpPerMeshInstanceOffset = TypedBuffer::create(instanceCount, Resource::BindFlags::ShaderResource); + mpPerMeshInstanceOffset->setName("LightCollection_PerMeshInstanceOffset"); + + const size_t sizeInBytes = triangleOffsets.size() * sizeof(triangleOffsets[0]); + assert(mpPerMeshInstanceOffset->getSize() == sizeInBytes); + mpPerMeshInstanceOffset->setBlob(triangleOffsets.data(), 0, sizeInBytes); + } + + void LightCollection::integrateEmissive(RenderContext* pRenderContext) + { + assert(mTriangleCount > 0); + assert(mMeshLights.size() > 0); + + // 1st pass: Rasterize emissive triangles in texture space to sum up their texels. + { + // Re-allocate result buffer if needed. + const uint32_t bufSize = mTriangleCount * sizeof(glm::vec4); + if (!mIntegrator.pResultBuffer || mIntegrator.pResultBuffer->getSize() < bufSize) + { + mIntegrator.pResultBuffer = Buffer::create(bufSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mIntegrator.pResultBuffer->setName("LightCollection_IntegratorResults"); + assert(mIntegrator.pResultBuffer); + } + + // Clear to zero before we start. + pRenderContext->clearUAV(mIntegrator.pResultBuffer->getUAV().get(), glm::vec4(0.f)); + + // Specialize the program and re-create the vars. + prepareProgram(mIntegrator.pProgram.get()); + mIntegrator.pVars = GraphicsVars::create(mIntegrator.pProgram.get()); + if (!mIntegrator.pVars) throw std::exception("Failed to create program vars"); + + // Bind our resources. + bool success = setIntoProgramVars(mIntegrator.pVars.get(), mIntegrator.pVars->getConstantBuffer("CB"), "gLights"); + assert(success); + + mIntegrator.pVars["gTexelSum"] = mIntegrator.pResultBuffer; + mIntegrator.pVars["gPointSampler"] = mIntegrator.pPointSampler; + + // Execute. + pRenderContext->draw(mIntegrator.pState.get(), mIntegrator.pVars.get(), mTriangleCount * 3, 0); + } + + // 2nd pass: Finalize the per-triangle flux values. + { + // Specialize the program and re-create the vars. + prepareProgram(mpFinalizeIntegration->getProgram().get()); + mpFinalizeIntegration->setVars(nullptr); // Re-create vars. + + // Bind our resources. + bool success = setIntoProgramVars(mpFinalizeIntegration->getVars().get(), mpFinalizeIntegration->getVars()->getConstantBuffer("FinalCB"), "gLights"); + assert(success); + + mpFinalizeIntegration["gTexelSum"] = mIntegrator.pResultBuffer; + mpFinalizeIntegration["gTriangleData"] = mpTriangleData; // TODO: This buffer is already bound by setIntoProgramVars, is it dangerous to alias it like this? + + // Execute. + assert(mpFinalizeIntegration->getThreadGroupSize().y == 1); + uint32_t rows = div_round_up(mTriangleCount, mpFinalizeIntegration->getThreadGroupSize().x); + mpFinalizeIntegration->execute(pRenderContext, mpFinalizeIntegration->getThreadGroupSize().x, rows); + } + } + + void LightCollection::computeStats() const + { + if (mStatsValid) return; + + // Read back the current data. This is potentially expensive. + syncCPUData(); + + // Stats on input data. + MeshLightStats stats; + stats.meshLightCount = (uint32_t)mMeshLights.size(); + stats.triangleCount = (uint32)mMeshLightTriangles.size(); + + uint32_t trianglesTotal = 0; + for (const auto& meshLight : mMeshLights) + { + if (meshLight.isTextured()) + { + stats.meshesTextured++; + stats.trianglesTextured += meshLight.triangleCount; + } + trianglesTotal += meshLight.triangleCount; + } + assert(trianglesTotal == stats.triangleCount); + + // Stats on pre-processed data. + for (const auto& tri : mMeshLightTriangles) + { + assert(tri.luminousFlux >= 0.f); + if (tri.luminousFlux == 0.f) + { + stats.trianglesCulled++; + } + else + { + // TODO: Currently we don't detect uniform radiance for textured lights, so just look at whether the mesh light is textured or not. + // This code will change when we tag individual triangles as textured vs non-textured. + if (mMeshLights[tri.lightIdx].isTextured()) stats.trianglesActiveTextured++; + else stats.trianglesActiveUniform++; + } + } + stats.trianglesActive = stats.trianglesActiveUniform + stats.trianglesActiveTextured; + + mMeshLightStats = stats; + mStatsValid = true; + } + + void LightCollection::buildTriangleList(RenderContext* pRenderContext) + { + assert(mMeshLights.size() > 0); + + // Bind scene. + mpTriangleListBuilder["gScene"] = mpScene->getParameterBlock(); + + // Bind our output buffers. + mpTriangleListBuilder["gVertexPosOutput"] = mpMeshLightsVertexPos; + mpTriangleListBuilder["gTexCoordsOutput"] = mpMeshLightsTexCoords; + mpTriangleListBuilder["gTriangleData"] = mpTriangleData; + + // TODO: Single dispatch over all emissive triangles instad of per-mesh dispatches. + // This code is not performance critical though, as it's currently only run once at init time. + for (uint32_t lightIdx = 0; lightIdx < mMeshLights.size(); ++lightIdx) + { + const MeshLightData& meshLight = mMeshLights[lightIdx]; + + mpTriangleListBuilder["PerMeshCB"]["gLightIdx"] = lightIdx; + mpTriangleListBuilder["PerMeshCB"]["gMeshInstanceID"] = meshLight.meshInstanceID; + mpTriangleListBuilder["PerMeshCB"]["gTriangleCount"] = meshLight.triangleCount; + mpTriangleListBuilder["PerMeshCB"]["gTriangleOffset"] = meshLight.triangleOffset; + + // as each kernel will write to non-overlapping parts of the output buffers. + mpTriangleListBuilder->execute(pRenderContext, meshLight.triangleCount, 1u, 1u); + } + } + + void LightCollection::updateTrianglePositions(RenderContext* pRenderContext, const std::vector& updatedLights) + { + // This pass pre-transforms all emissive triangles into world space and updates their area and face normals. + // It is executed if any geometry in the scene has moved, which is wasteful since it will update also things + // that didn't move. However, due to the CPU overhead of doing per-mesh dispatches as before, it's still faster. + // TODO: Test using ExecuteIndirect to do the dispatches on the GPU for only the updated meshes. + // Alternatively, upload the list of updated meshes and early out unnecessary threads at runtime. + assert(!updatedLights.empty()); + + // Bind scene. + mpTrianglePositionUpdater["gScene"] = mpScene->getParameterBlock(); + + // Bind our resources. + mpTrianglePositionUpdater["gVertexPosOutput"] = mpMeshLightsVertexPos; + mpTrianglePositionUpdater["gTriangleData"] = mpTriangleData; + mpTrianglePositionUpdater["gMeshData"] = mpMeshData; + mpTrianglePositionUpdater["PerMeshCB"]["gTriangleCount"] = mTriangleCount; + + // Run compute pass to update all triangles. + mpTrianglePositionUpdater->execute(pRenderContext, mTriangleCount, 1u, 1u); + + mCPUInvalidData |= (CPUOutOfDateFlags::Positions | CPUOutOfDateFlags::TriangleData); + mStagingBufferValid = false; + } + + bool LightCollection::setIntoBlockCommon(const ParameterBlock::SharedPtr& pBlock, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const + { + assert(pBlock); + assert(pCB); + + // Check that the struct exists. + if (pCB->getVariableOffset(varName) == ConstantBuffer::kInvalidOffset) + { + logError("LightCollection::setIntoBlockCommon() - Variable " + varName + " does not exist"); + return false; + } + std::string prefix = varName + "."; + + // Ok. The struct exists. + // In the following we validate it has the correct fields and set the data. + + // Set variables. + pCB[prefix + "triangleCount"] = mTriangleCount; + pCB[prefix + "meshCount"] = (uint32_t)mMeshLights.size(); + + // Bind mesh light triangles. + if (mTriangleCount > 0) + { + // Bind per-triangle data (these buffers must exist if triangle count is > 0). + pBlock[prefix + "meshLightsVertexPos"] = mpMeshLightsVertexPos; + pBlock[prefix + "meshLightsTexCoords"] = mpMeshLightsTexCoords; + pBlock[prefix + "triangleData"] = mpTriangleData; + } + + // Bind per-mesh instance triangle offsets (if buffer exists, reset binding otherwise). + // This buffer can exist even if mTriangleCount == 0. It would just be an array of kInvalidIndex then. + pBlock[prefix + "perMeshInstanceOffset"] = mpPerMeshInstanceOffset->asTypedBufferBase(); + + // Bind mesh lights array. + if (!mMeshLights.empty()) + { + static_assert(sizeof(MeshLightData) % (sizeof(float) * 4) == 0, "MeshLightData size should be a multiple of 16"); + + // Bind mesh data. + pBlock[prefix + "meshData"] = mpMeshData; + + // Validate that the emissive textures array exists and is of correct size. + auto pTexVar = pBlock->getReflection()->getResource(prefix + "emissiveTextures"); + assert(pTexVar && pTexVar->getType()->asArrayType()->getArraySize() == mMeshLights.size()); + + // Bind array of emissive textures. One texture per mesh light (can be the same for multiple lights). + // TODO: Index into a compact array of emissive textures instead. + for (size_t lightIdx = 0; lightIdx < mMeshLights.size(); lightIdx++) + { + const MeshInstanceData& instanceData = mpScene->getMeshInstance(mMeshLights[lightIdx].meshInstanceID); + const Material::SharedPtr& pMaterial = mpScene->getMaterial(instanceData.materialID); + assert(pMaterial); + + const std::string texVar = prefix + "emissiveTextures[" + std::to_string(lightIdx) + "]"; + pBlock[texVar] = pMaterial->getEmissiveTexture(); // May be nullptr + } + } + + // Set sampler state. We only have a single sampler that is used for all emissive textures. + if (mpSamplerState) + { + bool success = pBlock->setSampler(prefix + "samplerState", mpSamplerState); + assert(success); + } + + return true; + } + + void LightCollection::copyDataToStagingBuffer(RenderContext* pRenderContext) const + { + if (mStagingBufferValid) return; + + // Allocate staging buffer for readback. The data from our different GPU buffers is stored consecutively. + const size_t stagingSize = mpMeshLightsVertexPos->getSize() + mpMeshLightsTexCoords->getSize() + mpTriangleData->getSize(); + if (!mpStagingBuffer || mpStagingBuffer->getSize() < stagingSize) + { + mpStagingBuffer = Buffer::create(stagingSize, Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpStagingBuffer->setName("LightCollection_StagingBuffer"); + mCPUInvalidData = CPUOutOfDateFlags::All; + } + + // Schedule the copy operations for data that is invalid. + // Note that the staging buffer is allocated for the worst-case encountered so far. + // If the number of triangles ever decreases, we'll be copying unnecessary data. This currently doesn't happen as geometry is not added/removed from the scene. + // TODO: Update this code if we start removing geometry dynamically. + assert(mCPUInvalidData != CPUOutOfDateFlags::None); // We shouldn't get here unless at least some data is out of date. + bool copyPositions = (mCPUInvalidData & CPUOutOfDateFlags::Positions) == CPUOutOfDateFlags::Positions; + bool copyTexCoords = (mCPUInvalidData & CPUOutOfDateFlags::TexCoords) == CPUOutOfDateFlags::TexCoords; + bool copyTriangleData = (mCPUInvalidData & CPUOutOfDateFlags::TriangleData) == CPUOutOfDateFlags::TriangleData; + + if (copyPositions) pRenderContext->copyBufferRegion(mpStagingBuffer.get(), 0, mpMeshLightsVertexPos.get(), 0, mpMeshLightsVertexPos->getSize()); + if (copyTexCoords) pRenderContext->copyBufferRegion(mpStagingBuffer.get(), mpMeshLightsVertexPos->getSize(), mpMeshLightsTexCoords.get(), 0, mpMeshLightsTexCoords->getSize()); + if (copyTriangleData) pRenderContext->copyBufferRegion(mpStagingBuffer.get(), mpMeshLightsVertexPos->getSize() + mpMeshLightsTexCoords->getSize(), mpTriangleData.get(), 0, mpTriangleData->getSize()); + + // Submit command list and insert signal. + pRenderContext->flush(false); + mpStagingFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + + // Resize the CPU-side triangle list (array-of-structs) buffer and mark the data as invalid. + mMeshLightTriangles.resize(mTriangleCount); + + mStagingBufferValid = true; + } + + void LightCollection::syncCPUData() const + { + if (mCPUInvalidData == CPUOutOfDateFlags::None) return; + + // If the data has not yet been copied to the staging buffer, we have to do that first. + // This should normally have done by calling prepareSyncCPUData(). + if (!mStagingBufferValid) + { + logWarning("LightCollection::syncCPUData() performance warning - Call LightCollection::prepareSyncCPUData() ahead of time if possible"); + prepareSyncCPUData(gpDevice->getRenderContext()); + } + + // Wait for signal. + mpStagingFence->syncCpu(); + + assert(mStagingBufferValid); + const void* mappedData = mpStagingBuffer->map(Buffer::MapType::Read); + const glm::vec3* vertexPos = reinterpret_cast(mappedData); + const glm::vec2* vertexTexCrd = reinterpret_cast(reinterpret_cast(mappedData) + mpMeshLightsVertexPos->getSize()); + assert(mpTriangleData); + if (mpTriangleData->getElementSize() != sizeof(EmissiveTriangle)) throw std::exception("Struct EmissiveTriangle size mismatch between CPU/GPU"); + const EmissiveTriangle* triangleData = reinterpret_cast(reinterpret_cast(mappedData) + mpMeshLightsVertexPos->getSize() + mpMeshLightsTexCoords->getSize()); + + assert(mTriangleCount > 0); + const bool updatePositions = (mCPUInvalidData & CPUOutOfDateFlags::Positions) == CPUOutOfDateFlags::Positions; + const bool updateTexCoords = (mCPUInvalidData & CPUOutOfDateFlags::TexCoords) == CPUOutOfDateFlags::TexCoords; + const bool updateTriangleData = (mCPUInvalidData & CPUOutOfDateFlags::TriangleData) == CPUOutOfDateFlags::TriangleData; + + assert(mMeshLightTriangles.size() == (size_t)mTriangleCount); + for (uint32_t triIdx = 0; triIdx < mTriangleCount; triIdx++) + { + auto& tri = mMeshLightTriangles[triIdx]; + + // Store triangle data. + if (updateTriangleData) + { + tri.lightIdx = triangleData[triIdx].lightIdx; + tri.normal = triangleData[triIdx].normal; + tri.area = triangleData[triIdx].area; + tri.averageRadiance = triangleData[triIdx].averageRadiance; + tri.luminousFlux = triangleData[triIdx].flux; + } + + // Store texcoords. + if (updateTexCoords) + { + for (uint32_t j = 0; j < 3; j++) tri.vtx[j].uv = vertexTexCrd[triIdx * 3 + j]; + } + + // Store positions. + if (updatePositions) + { + for (uint32_t j = 0; j < 3; j++) tri.vtx[j].pos = vertexPos[triIdx * 3 + j]; + } + } + + mpStagingBuffer->unmap(); + + mCPUInvalidData = CPUOutOfDateFlags::None; + } +} diff --git a/Source/Falcor/Experimental/Scene/Lights/LightCollection.cs.slang b/Source/Falcor/Experimental/Scene/Lights/LightCollection.cs.slang new file mode 100644 index 000000000..4c1897897 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/LightCollection.cs.slang @@ -0,0 +1,164 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Utils/Math/MathConstants.slang" + +import HostDeviceData; +import Scene; +import Experimental.Scene.Lights.LightCollection; + +// TODO: Move the passes to separate .slang files to avoid confusion of which resources are bound, or +// use new Slang syntax for passing resources to the entry point when that's available. + +// Resources used by the finalizeIntegration pass. +cbuffer FinalCB +{ + LightCollection gLights; ///< The light collection. +} + +ByteAddressBuffer gTexelSum; ///< Sum over texels (RGB) + number of texels (A) in RGBA32Float format. + +// Resources used by the build/update passes. +cbuffer PerMeshCB +{ + uint gLightIdx; ///< The mesh light index. + uint gMeshInstanceID; ///< Mesh light's global mesh instance ID. We use this to find its data in the scene. + uint gTriangleCount; ///< Number of triangles in current mesh light. + uint gTriangleOffset; ///< Current offset into global list of emissive triangles. +} + +RWByteAddressBuffer gVertexPosOutput; ///< Vertex positions in world space for all mesh light triangles. Size: triangleCount * 3 * sizeof(float3). +RWByteAddressBuffer gTexCoordsOutput; ///< Texture coordinates for all mesh light triangles. Size: triangleCount * 3 * sizeof(float2). +StructuredBuffer gMeshData; ///< Per-mesh data for emissive meshes. Size. _NUM_MESH_LIGHTS * sizeof(MeshLightData). + +// Resources used by all passes. +RWStructuredBuffer gTriangleData; ///< Per-triangle data for emissive triangles. Size: triangleCount * sizeof(EmissiveTriangle). + + +/** Kernel building the emissive triangles list for all mesh lights. + One dispatch per mesh light with one thread per triangle. +*/ +[numthreads(256, 1, 1)] +void buildTriangleList(uint3 DTid : SV_DispatchThreadID) +{ + if (DTid.x >= gTriangleCount) return; + + const uint triangleIndex = DTid.x; // Triangle index in current mesh. + const uint triIdx = gTriangleOffset + triangleIndex; + const uint vtxIdx = triIdx * 3; // The emissive triangle list is non-indexed. + + // Store vertex data. + float3 p[3]; + float2 texC[3]; + gScene.getVertexPositionsW(gMeshInstanceID, triangleIndex, p); + gScene.getVertexTexCoords(gMeshInstanceID, triangleIndex, texC); + + [unroll] + for (int i = 0; i < 3; i++) + { + gVertexPosOutput.Store3((vtxIdx + i) * 12, asuint(p[i])); + gTexCoordsOutput.Store2((vtxIdx + i) * 8, asuint(texC[i])); + } + + // Store triangle data. + float triangleArea; + float3 faceNormal = gScene.computeFaceNormalAndAreaW(gMeshInstanceID, p, triangleArea); + + gTriangleData[triIdx].lightIdx = gLightIdx; + gTriangleData[triIdx].normal = faceNormal; + gTriangleData[triIdx].area = triangleArea; +} + +/** Kernel updating the emissive triangles for all mesh lights. + Single dispatch with one thread per triangle. +*/ +[numthreads(256, 1, 1)] +void updateTriangleVertices(uint3 DTid : SV_DispatchThreadID) +{ + const uint triIdx = DTid.x; // Global emissive triangle index. + const uint vtxIdx = triIdx * 3; // The emissive triangle list is non-indexed. + + if (triIdx >= gTriangleCount) return; + + // Get the data for the mesh that this triangle belongs to. + uint lightIdx = gTriangleData[triIdx].lightIdx; + const MeshLightData meshData = gMeshData[lightIdx]; + + uint meshInstanceID = meshData.meshInstanceID; + uint triangleIndex = triIdx - meshData.triangleOffset; // Local triangle index in the mesh + + // Update vertex data. + float3 p[3]; + gScene.getVertexPositionsW(meshInstanceID, triangleIndex, p); + + [unroll] + for (int i = 0; i < 3; i++) + { + gVertexPosOutput.Store3((vtxIdx + i) * 12, asuint(p[i])); + } + + // Update triangle data. + float triangleArea; + float3 faceNormal = gScene.computeFaceNormalAndAreaW(meshInstanceID, p, triangleArea); + + gTriangleData[triIdx].normal = faceNormal; + gTriangleData[triIdx].area = triangleArea; +} + +/** Kernel computing the final pre-integrated triangle average radiance and flux. + One dispatch with one thread per triangle (the dispatch is arranged as Y blocks of 256x1 threads). +*/ +[numthreads(256, 1, 1)] +void finalizeIntegration(uint3 DTid : SV_DispatchThreadID) +{ + const uint triIdx = DTid.y * 256 + DTid.x; + if (triIdx >= gLights.triangleCount) return; + + // Compute the triangle's average emitted radiance (RGB). + const MeshLightData lightData = gLights.getMeshData(triIdx); + float3 averageEmissiveColor = lightData.emissiveColor; + + if (lightData.isTextured()) + { + // Compute the triangle's average textured emissive color based on the pre-integration results. + // The alpha channel stores the texel count. If it is zero, the triangle didn't cover any samples + // and we don't know anything about its radiance. In that case, assign a default value (1.0) to avoid bias. + // TODO: This fallback can be removed when we use conservative rasterization in the pre-integration pass. + float4 texelSum = asfloat(gTexelSum.Load4(triIdx * 16u)); + float weight = texelSum.a; + averageEmissiveColor = weight > 0.f ? texelSum.rgb / weight : float3(1.0f); + } + float3 averageRadiance = averageEmissiveColor * lightData.emissiveFactor; + + // Pre-compute the luminous flux emitted, which is what we use during sampling to set probabilities. + // We assume diffuse emitters and integrate per side (hemisphere) => the scale factor is pi. + float area = gTriangleData[triIdx].area; // Triangle area in m^2 (the scene units are assumed to be in meters). + float flux = luminance(averageRadiance) * area * (float) M_PI; // Flux in lumens. + + gTriangleData[triIdx].averageRadiance = averageRadiance; + gTriangleData[triIdx].flux = flux; +} diff --git a/Source/Falcor/Experimental/Scene/Lights/LightCollection.h b/Source/Falcor/Experimental/Scene/Lights/LightCollection.h new file mode 100644 index 000000000..7a86d551e --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/LightCollection.h @@ -0,0 +1,265 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "MeshLightData.h" + +namespace Falcor +{ + /** Class that holds a collection of mesh lights for a scene. + + Each mesh light is represented by a mesh instance with an emissive material. + + This class has utility functions for updating and pre-processing the mesh lights. + The LightCollection can be used standalone, but more commonly it will be wrapped + by an emissive light sampler. + */ + class dlldecl LightCollection : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; + + static const uint32_t kInvalidIndex = -1; + + enum class UpdateFlags : uint32_t + { + None = 0u, ///< Nothing was changed. + MatrixChanged = 1u, ///< Mesh instance transform changed. + AnimationChanged = 2u, ///< Vertices changed due to animation. + }; + + struct UpdateStatus + { + std::vector lightsUpdateInfo; + }; + + struct MeshLightStats + { + // Stats before pre-processing (input data). + uint32_t meshLightCount = 0; ///< Number of mesh lights. + uint32_t triangleCount = 0; ///< Number of mesh light triangles (total). + uint32_t meshesTextured = 0; ///< Number of mesh lights with textured emissive. + uint32_t trianglesTextured = 0; ///< Number of mesh light triangles with textured emissive. + + // Stats after pre-processing (what's used during rendering). + uint32_t trianglesCulled = 0; ///< Number of triangles culled (due to zero radiance and/or area). + uint32_t trianglesActive = 0; ///< Number of active (non-culled) triangles. + uint32_t trianglesActiveUniform = 0; ///< Number of active triangles with const radiance. + uint32_t trianglesActiveTextured = 0; ///< Number of active triangles with textured radiance. + }; + + /** Represents one mesh light triangle vertex. + */ + struct MeshLightVertex + { + glm::vec3 pos; ///< World-space position. + glm::vec2 uv; ///< Texture coordinates in emissive texture (if textured). + }; + + /** Represents one mesh light triangle. + */ + struct MeshLightTriangle + { + // TODO: Perf of indexed vs non-indexed on GPU. We avoid level of indirection, but have more bandwidth non-indexed. + MeshLightVertex vtx[3]; ///< Vertices. These are non-indexed for now. + uint32_t lightIdx = kInvalidIndex; ///< Per-triangle index into mesh lights array. + + // Pre-computed quantities. + glm::vec3 normal = glm::vec3(0); ///< Triangle's face normal in world space. + glm::vec3 averageRadiance = glm::vec3(0); ///< Average radiance emitted over triangle. For textured emissive the radiance varies over the surface. + float luminousFlux = 0.f; ///< Pre-integrated luminous flux (lumens) emitted per side of the triangle for double-sided emitters (total flux is 2x this value). + float area = 0.f; ///< Triangle area in world space units. + + /** Returns the center of the triangle in world space. + */ + glm::vec3 getCenter() const + { + return (vtx[0].pos + vtx[1].pos + vtx[2].pos) / 3.0f; + } + }; + + + virtual ~LightCollection() = default; + + /** Creates a light collection for the given scene. + Note that update() must be called before the collection is ready to use. + \param[in] pRenderContext The render context. + \param[in] pScene The scene. + \return Ptr to the created object, or nullptr if an error occured. + */ + static SharedPtr create(RenderContext* pRenderContext, const Scene::SharedPtr& pScene); + + /** Updates the light collection to the current state of the scene. + \param[in] pRenderContext The render context. + \param[out] pUpdateStatus Stores information about which type of updates were performed for each mesh light. This is an optional output parameter. + \return True if the lighting in the scene has changed since the last frame. + */ + bool update(RenderContext* pRenderContext, UpdateStatus* pUpdateStatus = nullptr); + + /** Render the GUI. + \return True if options that may affect the rendering have changed. + */ + virtual bool renderUI(Gui::Widgets& widgets); + + /** Specialize a program to use this light collection. + Note that ProgramVars may need to be re-created after this call, check the return value. + \param[in] pProgram The Program to which macro definitions will be added. + \return True if the ProgramVars needs to be re-created. + */ + bool prepareProgram(ProgramBase* pProgram) const; + + /** Bind the light collection data to a program vars object. + The default implementation calls setIntoBlockCommon(). + Note that prepareProgram() must have been called before this function. + \param[in] pVars The program vars to set the data into. + \param[in] pCB The constant buffer to set the data into. + \param[in] varName The name of the data variable in the constant buffer. + \return True if successful, false otherwise. + */ + bool setIntoProgramVars(ProgramVars* pVars, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const { return setIntoBlockCommon(pVars->getDefaultBlock(), pCB, varName); } + + /** Bind the light collection data to a parameter block object. + The default implementation calls setIntoBlockCommon(). + Note that prepareProgram() must have been called before this function. + \param[in] pBlock The parameter block to set the data into. + \param[in] varName The name of the data variable in the parameter block. + \return True if successful, false otherwise. + */ + bool setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) const { return setIntoBlockCommon(pBlock, pBlock->getDefaultConstantBuffer(), varName); } + + /** Bind the light collection data to a given constant buffer in a parameter block. + Note that prepareProgram() must have been called before this function. + \param[in] pBlock The parameter block to set the data into (possibly the default parameter block). + \param[in] pCB The constant buffer in the parameter block to set the data into. + \param[in] varName The name of the data variable. + \return True if successful, false otherwise. + */ + bool setIntoBlockCommon(const ParameterBlock::SharedPtr& pBlock, const ConstantBuffer::SharedPtr& pCB, const std::string& varName) const; + + /** Returns the total number of active (non-culled) triangle lights. + */ + uint32_t getActiveLightCount() const { return getStats().trianglesActive; } + + /** Returns the total number of triangle lights (may include culled triangles). + */ + uint32_t getTotalLightCount() const { return mTriangleCount; } + + /** Returns stats. + */ + const MeshLightStats& getStats() const { computeStats(); return mMeshLightStats; } + + /** Returns a CPU buffer with all emissive triangles in world space. + Note that update() must have been called before for the data to be valid. + Call prepareSyncCPUData() ahead of time to avoid stalling the GPU. + */ + const std::vector& getMeshLightTriangles() const { syncCPUData(); return mMeshLightTriangles; } + + /** Returns a CPU buffer with all mesh lights. + Note that update() must have been called before for the data to be valid. + */ + const std::vector& getMeshLights() const { return mMeshLights; } + + /** Prepare for syncing the CPU data. + If the mesh light triangles will be accessed with getMeshLightTriangles() + performance can be improved by calling this function ahead of time. + This function schedules the copies so that it can be read back without delay later. + */ + void prepareSyncCPUData(RenderContext* pRenderContext) const { copyDataToStagingBuffer(pRenderContext); } + + // Internal update flags. This only public for enum_class_operators() to work. + enum class CPUOutOfDateFlags : uint32_t + { + None = 0u, + Positions = 1u << 0, + TexCoords = 1u << 1, + TriangleData = 1u << 2, + All = (1u << 3) - 1 + }; + + protected: + LightCollection() = default; + + bool init(RenderContext* pRenderContext, const Scene::SharedPtr& pScene); + bool initIntegrator(); + bool setupMeshLights(); + void build(RenderContext* pRenderContext); + void prepareTriangleData(RenderContext* pRenderContext); + void prepareMeshData(); + void integrateEmissive(RenderContext* pRenderContext); + void computeStats() const; + void buildTriangleList(RenderContext* pRenderContext); + void updateTrianglePositions(RenderContext* pRenderContext, const std::vector& updatedLights); + + void copyDataToStagingBuffer(RenderContext* pRenderContext) const; + void syncCPUData() const; + + // Internal state + Scene::SharedPtr mpScene; + + std::vector mMeshLights; ///< List of all mesh lights. + uint32_t mTriangleCount = 0; ///< Total number of triangles in all mesh lights (= mMeshLightTriangles.size()). This may include culled triangles. + + mutable std::vector mMeshLightTriangles; ///< List of all pre-processed mesh light triangles. + mutable MeshLightStats mMeshLightStats; ///< Stats before/after pre-processing of mesh lights. Do not access this directly, use getStats() which ensures the stats are up-to-date. + mutable bool mStatsValid = false; ///< True when stats are valid. + + // GPU resources for the mesh light vertex/triangle data. + // TODO: Perf of individual buffers vs StructuredBuffer? + // We should profile. The code would be simpler with StructuredBuffer. + Buffer::SharedPtr mpMeshLightsVertexPos; ///< Vertex positions in world space for all mesh light triangles (3 * mTriangleCount elements). + Buffer::SharedPtr mpMeshLightsTexCoords; ///< Texture coordinates for all mesh light triangles (3 * mTriangleCount elements). + StructuredBuffer::SharedPtr mpTriangleData; ///< Per-triangle data for emissive triangles (mTriangleCount elements). + StructuredBuffer::SharedPtr mpMeshData; ///< Per-mesh data for emissive meshes (mMeshLights.size() elements). + TypedBuffer::SharedPtr mpPerMeshInstanceOffset; ///< Per-mesh instance offset into emissive triangles array (Scene::getMeshInstanceCount() elements). + + mutable Buffer::SharedPtr mpStagingBuffer; ///< Staging buffer used for retrieving the vertex positions, texture coordinates and light IDs from the GPU. + GpuFence::SharedPtr mpStagingFence; ///< Fence used for waiting on the staging buffer being filled in. + + Sampler::SharedPtr mpSamplerState; ///< Material sampler for emissive textures. + + // Shader programs. + struct + { + GraphicsProgram::SharedPtr pProgram; + GraphicsVars::SharedPtr pVars; + GraphicsState::SharedPtr pState; + Sampler::SharedPtr pPointSampler; ///< Point sampler for fetching individual texels in integrator. Must use same wrap mode etc. as material sampler. + Buffer::SharedPtr pResultBuffer; ///< The output of the integration pass is written here. Using raw buffer for fp32 compatibility. + } mIntegrator; + + ComputePass::SharedPtr mpTriangleListBuilder; + ComputePass::SharedPtr mpTrianglePositionUpdater; + ComputePass::SharedPtr mpFinalizeIntegration; + + mutable CPUOutOfDateFlags mCPUInvalidData = CPUOutOfDateFlags::None; ///< Flags indicating which CPU data is valid. + mutable bool mStagingBufferValid = true; ///< Flag to indicate if the contents of the staging buffer is up-to-date. + }; + + enum_class_operators(LightCollection::CPUOutOfDateFlags); + enum_class_operators(LightCollection::UpdateFlags); +} diff --git a/Source/Falcor/Experimental/Scene/Lights/LightCollection.slang b/Source/Falcor/Experimental/Scene/Lights/LightCollection.slang new file mode 100644 index 000000000..f38a7884f --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/LightCollection.slang @@ -0,0 +1,169 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "LightCollectionShared.h" +#include "MeshLightData.h" + +import Shading; + +/** This struct represents all emissive geometry in the scene. + + All triangle vertices are pre-transformed to world space and important + quantities such as emitted flux are pre-computed. + + The user code instantiates LightCollection in e.g. a parameter block and + calls member functions on the CPU-side class to update/bind it to their program. +*/ +struct LightCollection +{ + uint triangleCount; ///< Total number of triangles in all mesh lights. + uint meshCount; ///< Total number of mesh lights. + + ByteAddressBuffer meshLightsVertexPos; ///< Vertex positions in world space for all mesh light triangles. Size: triangleCount * 3 * sizeof(float3). + ByteAddressBuffer meshLightsTexCoords; ///< Texture coordinates for all mesh light triangles. Size: triangleCount * 3 * sizeof(float2). + Buffer perMeshInstanceOffset; ///< Per-mesh instance offset into emissive triangles array. Size: meshInstanceCount * sizeof(uint). + StructuredBuffer triangleData; ///< Per-triangle data for emissive triangles. Size: triangleCount * sizeof(EmissiveTriangle). + StructuredBuffer meshData; ///< Per-mesh data for emissive meshes. Size. _NUM_MESH_LIGHTS * sizeof(MeshLightData). + +#if _NUM_MESH_LIGHTS > 0 + // TODO: Replace emissive textures array by materialID indices into the Scene's material array. + Texture2D emissiveTextures[_NUM_MESH_LIGHTS]; ///< Per-mesh light emissive textures, or null if not textured. +#endif + SamplerState samplerState; ///< Sampler state to be used for textured lights. + + // Accessors + + /** Returns the total number of emissive triangles. + */ + uint getTriangleCount() { return triangleCount; } + + /** Returns the data for a given triangle. + */ + EmissiveTriangle getTriangleData(uint triIdx) + { + return triangleData[triIdx]; + } + + /** Returns the mesh light index for a given triangle. + */ + uint getLightIdx(uint triIdx) + { + return triangleData[triIdx].lightIdx; + } + + /** Returns the mesh light data for a given emissive triangle. + \param[in] triIdx Emissive triangle index. + \return Mesh light data. + */ + MeshLightData getMeshData(uint triIdx) + { +#if _NUM_MESH_LIGHTS > 0 + return meshData[getLightIdx(triIdx)]; +#else + MeshLightData data; + data.init(); + return data; +#endif + } + + /** Returns world space vertex position based on global vertex index. + Note that the vertex buffer is non-indexed. + */ + float3 getVtxPos(uint vtxIdx) + { + uint address = (vtxIdx * 3) * 4; // The data is float3 (3*4B per vertex). + return asfloat(meshLightsVertexPos.Load3(address)); + } + + /** Returns texture coordinate based on global vertex index. + Note that the vertex buffer is non-indexed. + */ + float2 getVtxTexCoord(uint vtxIdx) + { + int address = (vtxIdx * 2) * 4; // The data is float2 (2*4B per vertex). + return asfloat(meshLightsTexCoords.Load2(address)); + } + + /** Returns a triangle's vertex positions in world space. + \param[in] triIdx Emissive triangle index. + \param[out] p Position of vertex 0,1,2 in world space. + */ + void getVtxPositions(uint triIdx, out float3 p[3]) + { + p[0] = getVtxPos(triIdx * 3 + 0); + p[1] = getVtxPos(triIdx * 3 + 1); + p[2] = getVtxPos(triIdx * 3 + 2); + } + + /** Returns interpolated position on a given triangle. + \param[in] triIdx Emissive triangle index. + \param[in] barycentrics Barycentric coordinates. + \return Interpolated position in world space. + */ + float3 getPosition(uint triIdx, float3 barycentrics) + { + return getVtxPos(triIdx * 3 + 0) * barycentrics[0] + + getVtxPos(triIdx * 3 + 1) * barycentrics[1] + + getVtxPos(triIdx * 3 + 2) * barycentrics[2]; + } + + /** Returns interpolated texture coordinate on a given triangle. + \param[in] triIdx Emissive triangle index. + \param[in] barycentrics Barycentric coordinates. + \return Interpolated texture coordinate. + */ + float2 getTexCoord(uint triIdx, float3 barycentrics) + { + return getVtxTexCoord(triIdx * 3 + 0) * barycentrics[0] + + getVtxTexCoord(triIdx * 3 + 1) * barycentrics[1] + + getVtxTexCoord(triIdx * 3 + 2) * barycentrics[2]; + } + +#if _NUM_MESH_LIGHTS > 0 + /** Evaluates the outgoing radiance on the front-facing side of a given triangle. + This function samples the triangle's emissive texture if its textured. + Note the caller check the facing status themselves to discard back-facing hits. + \param[in] triIdx Emissive triangle index. + \param[in] barycentrics Barycentric coordinates. + \return Evaluated outgoing radiance on the front-facing side. + */ + float3 getEmissive(uint triIdx, float3 barycentrics) + { + // Get the mesh light index. We need this below to fetch material properties + // in order to evaluate the triangles emissive color (possibly textured). + uint lightIndex = getLightIdx(triIdx); + + // Interpolate the tex coords. + float2 uv = getTexCoord(triIdx, barycentrics); + + // Sample emitted radiance. + ExplicitLodTextureSampler lod = { 0.f }; + float3 emissiveColor = meshData[lightIndex].emissiveColor; + return sampleTexture(emissiveTextures[lightIndex], samplerState, uv, float4(emissiveColor, 1.0f), EXTRACT_EMISSIVE_TYPE(meshData[lightIndex].flags), lod).rgb * meshData[lightIndex].emissiveFactor; + } +#endif +}; diff --git a/Source/Falcor/Experimental/Scene/Lights/LightCollectionShared.h b/Source/Falcor/Experimental/Scene/Lights/LightCollectionShared.h new file mode 100644 index 000000000..c03901772 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/LightCollectionShared.h @@ -0,0 +1,45 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Data/HostDeviceData.h" + +BEGIN_NAMESPACE_FALCOR + +/** Per-triangle data for emissive triangles. + This struct is shared between the CPU/GPU. +*/ +struct EmissiveTriangle +{ + uint lightIdx; ///< Index into global mesh lights array. + float3 normal; ///< Face normal in world space. + float area; ///< Triangle area in world space. + float3 averageRadiance; ///< Average emitted radiance over the triangle. + float flux; ///< Pre-integrated luminous flux for triangle in lumens. +}; + +END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Experimental/Scene/Lights/LightHelpers.slang b/Source/Falcor/Experimental/Scene/Lights/LightHelpers.slang new file mode 100644 index 000000000..834ac174b --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/LightHelpers.slang @@ -0,0 +1,247 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** This file contains helper functions for analytic light source sampling. + The code supports Falcor's analytic point, directional, and area lights, + which are all defined in the scene description. + + Mesh lights (emissive geometry) and light probes are handled separately. + + This is work in progress. The code is not very well-tested. +*/ + +#include "Utils/Math/MathConstants.slang" +#include "Data/HostDeviceData.h" + +import Utils.Math.MathHelpers; + +static const float kMinLightDistSqr = 1e-9f; +static const float kMaxLightDistance = FLT_MAX; + +/** Describes a light sample for Falcor's analytic light sources. + + The struct contains a superset of what is normally needed for evaluating + lighting integrals. Be careful not to access fields that are not needed, + to make sure dead code elimination removes the computations. + The Li field is nonzero only if the sample is valid (no need to check the pdf). +*/ +struct AnalyticLightSample +{ + float3 posW; ///< Sampled point on the light source in world space (for local lights only). + float3 normalW; ///< Normal of the sampled point on the light source in world space (normalized). + float3 dir; ///< Direction from the shading point to the sampled point on the light in world space (normalized). + float distance; ///< Distance from the shading point to sampled point on the light. + float3 Li; ///< Incident radiance at the shading point (unshadowed). Note: Already divided by the pdf. + float pdf; ///< Probability density function with respect to solid angle at the shading point (pdf == 0 for invalid samples). + + static AnalyticLightSample init() + { + AnalyticLightSample ls; + ls.posW = float3(0, 0, 0); + ls.normalW = float3(0, 0, 0); + ls.dir = float3(0, 0, 0); + ls.distance = 0; + ls.Li = float3(0, 0, 0); + ls.pdf = 0; + return ls; + } +}; + +/** Internal helper function to finalize the shared computations for area light samples. + The input sample must already have posW and normalW computed. +*/ +void finalizeAreaLightSample(const float3 shadingPosW, const LightData light, inout AnalyticLightSample ls) +{ + // Compute direction and distance to light. + // The distance is clamped to a small epsilon to avoid div-by-zero below. + float3 toLight = ls.posW - shadingPosW; + float distSqr = max(dot(toLight, toLight), kMinLightDistSqr); + ls.distance = sqrt(distSqr); + ls.dir = toLight / ls.distance; + + // Compute incident radiance at shading point. + // The area lights are single-sided by default, so radiance is zero when seen from the back-facing side. + float cosTheta = saturate(dot(ls.normalW, -ls.dir)); + ls.Li = light.intensity * (light.surfaceArea * cosTheta / distSqr); + + // Compute the PDF with respect to solid angle. Note this may be +inf. + ls.pdf = distSqr / (cosTheta * light.surfaceArea); +} + +/** Samples a rectangular area light source. + \param[in] shadingPosW Shading point in world space. + \param[in] light Light data. + \param[in] u Uniform random number in [0,1)^2. + \param[out] ls Light sample struct. +*/ +void sampleRectAreaLight(const float3 shadingPosW, const LightData light, const float2 u, out AnalyticLightSample ls) +{ + // Pick a random sample on the quad. + // The quad is from (-1,-1,0) to (1,1,0) in object space, but may be scaled by its transform matrix. + float3 pos = float3(u.x * 2.f - 1.f, u.y * 2.f - 1.f, 0.f); + + // Apply model to world transformation matrix. + ls.posW = mul(float4(pos, 1.f), light.transMat).xyz; + + // Setup world space normal. + float3 tangentW = mul(float4(1.f, 0.f, 0.f, 0.f), light.transMat).xyz; + float3 bitangentW = mul(float4(0.f, 1.f, 0.f, 0.f), light.transMat).xyz; + // TODO: normalW is not correctly oriented for mesh instances that have flipped triangle winding. + ls.normalW = normalize(cross(tangentW, bitangentW)); + + finalizeAreaLightSample(shadingPosW, light, ls); +} + +/** Samples a spherical area light source. + \param[in] shadingPosW Shading point in world space. + \param[in] light Light data. + \param[in] u Uniform random number in [0,1)^2. + \param[out] ls Light sample struct. +*/ +void sampleSphereAreaLight(const float3 shadingPosW, const LightData light, const float2 u, out AnalyticLightSample ls) +{ + // Sample a random point on the sphere. + // TODO: We should pick a random point on the hemisphere facing the shading point. + float3 pos = sample_sphere(u); + + // Apply model to world transformation matrix. + ls.posW = mul(float4(pos, 1.f), light.transMat).xyz; + + // Setup world space normal. + ls.normalW = normalize(mul(float4(pos, 0.f), light.transMatIT).xyz); + + finalizeAreaLightSample(shadingPosW, light, ls); +} + +/** Samples disc area light source. + \param[in] shadingPosW Shading point in world space. + \param[in] light Light data. + \param[in] u Uniform random number in [0,1)^2. + \param[out] ls Light sample struct. +*/ +void sampleDiscAreaLight(const float3 shadingPosW, const LightData light, const float2 u, out AnalyticLightSample ls) +{ + // Sample a random point on the disk. + // TODO: Fix spelling disagreement between disc vs disk. + float3 pos = float3(sample_disk(u), 0.f); + + // Transform to world space. + ls.posW = mul(float4(pos, 1.f), light.transMat).xyz; + + // Setup world space normal. + ls.normalW = normalize(mul(float4(0.f, 0.f, 1.f, 0.f), light.transMatIT).xyz); + + finalizeAreaLightSample(shadingPosW, light, ls); +} + +/** Samples a directional light source. + \param[in] shadingPosW Shading point in world space. + \param[in] light Light data. + \param[out] ls Light sample struct. +*/ +void sampleDirectionalLight(const float3 shadingPosW, const LightData light, out AnalyticLightSample ls) +{ + // A directional light doesn't have a position. Just clear to zero. + ls.posW = float3(0, 0, 0); + + // For a directional light, the normal is always along its light direction. + ls.normalW = light.dirW; + + // Setup direction and distance to light. + ls.distance = kMaxLightDistance; + ls.dir = -light.dirW; + + // Setup incident radiance. For directional lights there is no falloff or cosine term. + ls.Li = light.intensity; + + // For a directional light, the PDF with respect to solid angle is a Dirac function. Set to zero. + ls.pdf = 0.f; +} + +/** Samples a point (spot) light. + \param[in] shadingPosW Shading point in world space. + \param[in] light Light data. + \param[out] ls Light sample struct. +*/ +void samplePointLight(const float3 shadingPosW, const LightData light, out AnalyticLightSample ls) +{ + // Get the position and normal. + ls.posW = light.posW; + ls.normalW = light.dirW; + + // Compute direction and distance to light. + // The distance is clamped to a small epsilon to avoid div-by-zero below. + float3 toLight = ls.posW - shadingPosW; + float distSqr = max(dot(toLight, toLight), kMinLightDistSqr); + ls.distance = sqrt(distSqr); + ls.dir = toLight / ls.distance; + + // Compute incident radiance at shading point. + // TODO: Support spot lights. We assume uniform emission for now. + ls.Li = light.intensity / distSqr; + + // For a point light, the PDF with respect to solid angle is a Dirac function. Set to zero. + ls.pdf = 0.f; +} + +/** Samples an analytic light source. + This function calls the correct sampling function depending on the type of light. + Note: We get incorrect uninitialized variable warnings when compiled with fxc (sm5.1). Use sm6.0 or higher. + \param[in] shadingPosW Shading point in world space. + \param[in] light Light data. + \param[in] u Uniform random number in [0,1)^2. + \param[out] ls Sampled point on the light and associated sample data. +*/ +void sampleLight(const float3 shadingPosW, const LightData light, const float2 u, out AnalyticLightSample ls) +{ + // Sample the light based on its type: point, directional, or area. + switch (light.type) + { + case LightPoint: + samplePointLight(shadingPosW, light, ls); + break; + case LightDirectional: + sampleDirectionalLight(shadingPosW, light, ls); + break; + case LightAreaRect: + sampleRectAreaLight(shadingPosW, light, u, ls); + break; + case LightAreaSphere: + sampleSphereAreaLight(shadingPosW, light, u, ls); + break; + case LightAreaDisc: + sampleDiscAreaLight(shadingPosW, light, u, ls); + break; + case LightArea: + // Mesh area lights are treated separately by Falcor, should not exist in LightData. + // Fall through to default case that returns a null sample. + default: + ls = AnalyticLightSample.init(); + break; + } +} diff --git a/Source/Falcor/Experimental/Scene/Lights/MeshLightData.h b/Source/Falcor/Experimental/Scene/Lights/MeshLightData.h new file mode 100644 index 000000000..e516427c0 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Lights/MeshLightData.h @@ -0,0 +1,75 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Data/HostDeviceData.h" + +BEGIN_NAMESPACE_FALCOR + +static const uint kInvalidIndex = 0xffffffff; + +/** Describes a mesh area light. + + Note that due to mesh merging etc. it's not guaranteed that a mesh light + represents a single localized light source in the scene. + We should not make any assumptions of the extents or geometry of a mesh light. +*/ +struct MeshLightData +{ + uint meshInstanceID DEFAULTS(kInvalidIndex); ///< Mesh instance ID in the scene (= getGlobalHitID()). + uint triangleOffset DEFAULTS(kInvalidIndex); ///< Offset into LightCollection's global list of emissive triangles. + uint triangleCount DEFAULTS(0); ///< Number of triangles in mesh light. + uint flags DEFAULTS(0); ///< Flags for the material system. + + float3 emissiveColor DEFAULTS(float3(0.f)); ///< Material emissive color. This will be overridden by the emissive texture if it exists. + float emissiveFactor DEFAULTS(1.0f); ///< Material emissive multiplication factor. The emitted radiance is emissiveColor * emissiveFactor. + +#ifdef HOST_CODE + MeshLightData() { init(); } +#endif + + /** Initialize struct to all default values. + */ + SETTER_DECL void init() + { + meshInstanceID = kInvalidIndex; + triangleOffset = kInvalidIndex; + triangleCount = 0; + flags = 0; + emissiveColor = {}; + emissiveFactor = 1.f; + } + + /** Returns true if light uses an emissive texture. + */ + bool isTextured() CONST_FUNCTION + { + return EXTRACT_EMISSIVE_TYPE(flags) == ChannelTypeTexture; + } +}; + +END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Experimental/Scene/Material/MaterialHelpers.slang b/Source/Falcor/Experimental/Scene/Material/MaterialHelpers.slang new file mode 100644 index 000000000..56580c836 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Material/MaterialHelpers.slang @@ -0,0 +1,207 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Helper functions for the material/shading system. + + The data needed to shade a hit point consists of two parts (jointly represented by Falcor's 'ShadingData' struct): + 1) geometry parameters (position, normal etc.) + 2) material parameters (diffuse color, opacity etc.) + + This file defines a struct 'GeometryParams' to represent the hit point geometry. + This is useful in passes that don't need the material parameters (for example, for shadows or AO). + Another struct 'MaterialParams' provides a compact representation of the material parameters, + suitable for storing in the G-buffer. +*/ + +__exported import Shading; + +/** Struct representing the material parameters at a hit point. +*/ +struct MaterialParams +{ + float4 diffuseOpacity; ///< Diffuse color (rgb) and opacity (a). + float4 specularRoughness; ///< Specular color (rgb) and roughness (a). + float4 emissive; ///< Emissive color (rgb). TODO: Use the alpha channel either for a luminance scaler, or 1.0/IoR. + float4 extraParams; ///< Extra parameters (IoR, doubleSided, 0, 0). + + /** Returns the roughness parameter. + */ + float getRoughness() { return specularRoughness.w; } + + /** Initialize MaterialParams to its default values. + TODO: Try new Slang "setter" functionality. + */ + MaterialParams init() + { + MaterialParams params; + params.diffuseOpacity = float4(0, 0, 0, 1); + params.specularRoughness = float4(0, 0, 0, 1); + params.emissive = float4(0, 0, 0, 0); + params.extraParams = float4(1, 1, 0, 0); + return params; + } +}; + +/** Struct representing the geometry at a hit point, plus a few related fields. +*/ +struct GeometryParams +{ + float3 posW; ///< World-space position. + float3 V; ///< Normalized view direction in world space. + float2 uv; ///< Texture coordinate at hit point, or 0 if not available. + float3 faceN; ///< Face normal in world space. + + // Surface frame in world space. The axes (T,B,N) form a right-handed orthonormal basis. + // The normal and bitangent are normally provided by the framework, while tangent is computed based on those. + float3 N; ///< Shading normal (z-axis in local frame). + float3 B; ///< Shading bitangent (y-axis in local frame). + float3 T; ///< Shading tangent (x-axis in local frame). +}; + +/****************************************************************************** + Material helpers +******************************************************************************/ + +/** Extract material params from ShadingData struct. +*/ +MaterialParams getMaterialParams(ShadingData sd) +{ + MaterialParams matParams; + matParams.diffuseOpacity = float4(sd.diffuse, sd.opacity); + matParams.specularRoughness = float4(sd.specular, sd.linearRoughness); + matParams.emissive = float4(sd.emissive, 0.f); + matParams.extraParams = float4(sd.IoR, sd.doubleSided ? 1.f : 0.f, 0.f, 0.f); + return matParams; +} + +/****************************************************************************** + Geometry helpers +******************************************************************************/ + +/** Helper function for preparing a GeometryParams struct based on the local geometry. + Note that the input vectors must not be (0,0,0) as the normalizations would cause NaNs. + \param[in] worldPos World space position. + \param[in] viewDir View direction (unnormalized). + \param[in] normal Shading normal (unnormalized). + \param[in] bitangent Shading bitangent (unnormalized). + \param[in] faceNormal Face normal (normalized). + \param[in] texCrd Texture uv coordinate, or (0,0) if not available. + \return GeometryParams struct. +*/ +GeometryParams prepareGeometryParams(float3 worldPos, float3 viewDir, float3 normal, float3 bitangent, float3 faceNormal, float2 texCrd = float2(0, 0)) +{ + GeometryParams geoParams; + geoParams.posW = worldPos; + geoParams.V = normalize(viewDir); + geoParams.N = normalize(normal); + geoParams.B = normalize(bitangent - geoParams.N * dot(bitangent, geoParams.N)); + geoParams.T = cross(geoParams.B, geoParams.N); + geoParams.uv = texCrd; + geoParams.faceN = faceNormal; + return geoParams; +} + +/** Extract geometry params from ShadingData struct. +*/ +GeometryParams getGeometryParams(ShadingData sd) +{ + GeometryParams geoParams; + geoParams.posW = sd.posW; + geoParams.V = sd.V; + geoParams.N = sd.N; + geoParams.T = sd.T; + geoParams.B = sd.B; + geoParams.uv = sd.uv; + geoParams.faceN = sd.faceN; + return geoParams; +} + +/** Helper function for populating the geometry and material parameters of a ShadingData struct. +*/ +ShadingData prepareShadingData(GeometryParams geoParams, MaterialParams matParams) +{ + ShadingData sd = {}; + + sd.posW = geoParams.posW; + sd.V = geoParams.V; + sd.N = geoParams.N; + sd.B = geoParams.B; + sd.T = geoParams.T; + sd.uv = geoParams.uv; + + // Precompute N*V the same way as in Falcor (no clamp/epsilon). + sd.NdotV = dot(sd.N, sd.V); + sd.faceN = geoParams.faceN; + sd.frontFacing = dot(sd.V, sd.faceN) >= 0.f; + + sd.diffuse = matParams.diffuseOpacity.rgb; + sd.opacity = matParams.diffuseOpacity.a; + sd.specular = matParams.specularRoughness.rgb; + sd.linearRoughness = matParams.specularRoughness.a; + sd.ggxAlpha = sd.linearRoughness * sd.linearRoughness; + sd.emissive = matParams.emissive.rgb; + sd.IoR = matParams.extraParams.x; + sd.doubleSided = matParams.extraParams.y != 0.f; + + // Flip the normal if it's backfacing. + if (sd.NdotV <= 0 && sd.doubleSided) + { + sd.N = -sd.N; + sd.NdotV = -sd.NdotV; + } + + return sd; +} + +/** Helper function to transform vector v from the local surface frame to world space. +*/ +float3 fromLocal(float3 v, ShadingData sd) +{ + return sd.T * v.x + sd.B * v.y + sd.N * v.z; +} + +/** Helper function to transform vector v from the local surface frame to world space. +*/ +float3 fromLocal(float3 v, GeometryParams sd) +{ + return sd.T * v.x + sd.B * v.y + sd.N * v.z; +} + +/** Helper function to transform world space vector v to the local surface frame. +*/ +float3 toLocal(float3 v, ShadingData sd) +{ + return float3(dot(v, sd.T), dot(v, sd.B), dot(v, sd.N)); +} + +/** Helper function to transform world space vector v to the local surface frame. +*/ +float3 toLocal(float3 v, GeometryParams sd) +{ + return float3(dot(v, sd.T), dot(v, sd.B), dot(v, sd.N)); +} diff --git a/Source/Falcor/Experimental/Scene/Material/MaterialShading.slang b/Source/Falcor/Experimental/Scene/Material/MaterialShading.slang new file mode 100644 index 000000000..0cd143de0 --- /dev/null +++ b/Source/Falcor/Experimental/Scene/Material/MaterialShading.slang @@ -0,0 +1,1053 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** This file contains BRDF evaluation and importance sampling functions. + + It is work in progress. +*/ + +#include "Utils/Math/MathConstants.slang" + +__import Shading; +__import Utils.Math.MathHelpers; +__exported import Utils.Sampling.SampleGenerator; +__exported import Experimental.Scene.Material.MaterialHelpers; + +// NOTE: These are defined in BRDF.slang, but macro definitions are not exported so not visible here! +#define DiffuseBrdfLambert 0 +#define DiffuseBrdfDisney 1 +#define DiffuseBrdfFrostbite 2 + +#define DiffuseBrdf DiffuseBrdfFrostbite + +#define SpecularMaskingFunctionSmithGGXSeparable 0 ///< Used by UE4. +#define SpecularMaskingFunctionSmithGGXCorrelated 1 ///< Used by Frostbite. This is the more accurate form (default). + +#define SpecularMaskingFunction SpecularMaskingFunctionSmithGGXCorrelated + +// Disable sampling of the specular component for debugging +//#define DisableSpecularSampling + +// TODO: Remove this, replace by accurately derived epsilons where needed. +#define epsilon 1e-5f + +// We clamp the GGX width parameter (alpha_g) to avoid numerical instability. +// In some computations, we can avoid clamps etc. if 1.0 - alpha^2 != 1.0, so the epsilon should be 1.72666361e-4 or larger in fp32. +#define epsilon_alpha_g 0.001f + +// Minimum cos(theta) for the view and light vectors. +// A few functions are not robust for cos(theta) == 0.0. +// TODO: Derive appropriate bounds +static const float kMinCosTheta = 1e-6f; + +// -------------------------------------------------------------- +// TODO: Most function below exist in two versions, one for Falcor's BRDF (Frostbite) and one for the original Disney BRDF. +// We should refactor this code to use Slang interfaces and share common functions. +// We likely want something similar to C++ inheritance for this, so that we can have shared implementations in the "base class", +// and override the parts that are specific to the different models. Talk to Tim about this. +// -------------------------------------------------------------- + +/******************************************************************* + BSDF evaluation functions +*******************************************************************/ + +/** Evaluates the Fresnel term using Schlick's approximation. + Introduced in http://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf + + The Fresnel term equals f0 at normal incidence, and approaches f90=1.0 at 90 degrees. + The formulation below is generalized to allow both f0 and f90 to be specified. + + \param[in] f0 Specular reflectance at normal incidence (0 degrees). + \param[in] f90 Reflectance at orthogonal incidence (90 degrees), which should be 1.0 for specular surface reflection. + \param[in] cosTheta Cosine of angle between microfacet normal and incident direction (LdotH). + \return Fresnel term. +*/ +float3 evalFresnelSchlick(float3 f0, float3 f90, float cosTheta) +{ + return f0 + (f90 - f0) * pow(1 - cosTheta, 5); +} + +/** Evaluates the Lambertian BRDF. + + \param[in] sd Shading point data. + \return f_d +*/ +float3 evalDiffuseLambert(const ShadingData sd) +{ + return sd.diffuse.rgb * (1 / M_PI); +} + +/** Disney's diffuse term. + Based on https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf + + \param[in] sd Shading point data. + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] LdotH Dot product between half vector and incident direction, in positive hemisphere. + \return f_d +*/ +float3 evalDiffuseDisney(const ShadingData sd, float NdotL, float NdotV, float LdotH) +{ + float fd90 = 0.5 + 2 * LdotH * LdotH * sd.linearRoughness; + float fd0 = 1; + float lightScatter = evalFresnelSchlick(fd0, fd90, NdotL).r; + float viewScatter = evalFresnelSchlick(fd0, fd90, NdotV).r; + return sd.diffuse.rgb * (viewScatter * lightScatter * (1 / M_PI)); +} + +/** Frostbites's diffuse term. + This is Disney's diffuse BRDF with an ad-hoc normalization factor to ensure energy conservation. + Based on https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + + \param[in] sd Shading point data. + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] LdotH Dot product between half vector and incident direction, in positive hemisphere. + \return f_d +*/ +float3 evalDiffuseFrostbite(const ShadingData sd, float NdotL, float NdotV, float LdotH) +{ + float energyBias = lerp(0, 0.5, sd.linearRoughness); + float energyFactor = lerp(1, 1.0 / 1.51, sd.linearRoughness); + float fd90 = energyBias + 2 * LdotH * LdotH * sd.linearRoughness; + float fd0 = 1; + float lightScatter = evalFresnelSchlick(fd0, fd90, NdotL).r; + float viewScatter = evalFresnelSchlick(fd0, fd90, NdotV).r; + return sd.diffuse.rgb * (viewScatter * lightScatter * energyFactor * (1 / M_PI)); +} + +/** Evaluates the diffuse component (f_d) of the BRDF based on currently configured model. + + \param[in] sd Shading point data. + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] LdotH Dot product between half vector and incident direction, in positive hemisphere. + \return f_d +*/ +float3 evalDiffuse(const ShadingData sd, float NdotL, float NdotV, float LdotH) +{ +#if DiffuseBrdf == DiffuseBrdfLambert + return evalDiffuseLambert(sd); +#elif DiffuseBrdf == DiffuseBrdfDisney + return evalDiffuseDisney(sd, NdotL, NdotV, LdotH); +#elif DiffuseBrdf == DiffuseBrdfFrostbite + return evalDiffuseFrostbite(sd, NdotL, NdotV, LdotH); +#endif +} + +/** Evaluates the GGX (Trowbridge-Reitz) normal distribution function (D). + + Introduced by Trowbridge and Reitz, "Average irregularity representation of a rough surface for ray reflection", Journal of the Optical Society of America, vol. 65(5), 1975. + See the correct normalization factor in Walter et al. https://dl.acm.org/citation.cfm?id=2383874 + We use the simpler, but equivalent expression in Eqn 19 from http://blog.selfshadow.com/publications/s2012-shading-course/hoffman/s2012_pbs_physics_math_notes.pdf + + For microfacet models, D is evaluated for the direction h to find the density of potentially active microfacets (those for which microfacet normal m = h). + The 'alpha' parameter is the standard GGX width, e.g., it is the square of the linear roughness parameter in Disney's BRDF. + Note there is a singularity (0/0 = NaN) at NdotH = 1 and alpha = 0, so alpha should be clamped to some epsilon. + + \param[in] NdotH Dot product between shading normal and half vector, in positive hemisphere. + \param[in] alpha GGX width parameter (should be clamped to small epsilon beforehand). + \return D(H) +*/ +float evalNdfGGX(float NdotH, float alpha) +{ + float a2 = alpha * alpha; + float d = ((NdotH * a2 - NdotH) * NdotH + 1); + return a2 / (d * d * M_PI); +} + +/** Evaluates the separable form of the masking-shadowing function for the GGX normal distribution, using Smith's approximation. + + This optimized form evaluates Equation 98 in http://jcgt.org/published/0003/02/03/paper.pdf divided by (4 * NdotL * NdotV), + and is used in UE4 according to http://graphicrants.blogspot.fi/2013/08/specular-brdf-reference.html + The function is only valid for V and L in the positive hemisphere, and should be clamped to 0 otherwise. + + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] alpha GGX width parameter (should be clamped to small epsilon beforehand). + \return G(L,V,N) / (4 * NdotL * NdotV) +*/ +float evalMaskingSmithGGXSeparable(float NdotL, float NdotV, float alpha) +{ + float a2 = alpha * alpha; + float lambdaV = NdotV + sqrt((-NdotV * a2 + NdotV) * NdotV + a2); + float lambdaL = NdotL + sqrt((-NdotL * a2 + NdotL) * NdotL + a2); + return 1.f / (lambdaV * lambdaL); +} + +/** Evaluates the height-correlated form of the masking-shadowing function for the GGX normal distribution, using Smith's approximation. + + This optimized form evaluates Equation 99 in http://jcgt.org/published/0003/02/03/paper.pdf divided by (4 * NdotL * NdotV). + Eric Heitz recommends using it in favor of the separable form as it is more accurate and of similar complexity. + The function is only valid for V and L in the positive hemisphere, and should be clamped to 0 otherwise. + + \note The function is +inf if NdotL = NdotV = 0. The dot products should be clamped to small epsilon beforehand. + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] alpha GGX width parameter (should be clamped to small epsilon beforehand). + \return G(L,V,N) / (4 * NdotL * NdotV) +*/ +float evalMaskingSmithGGXCorrelated(float NdotL, float NdotV, float alpha) +{ + float a2 = alpha * alpha; + float lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2); // V,L should be flipped, it's not a mistake + float lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2); + return 0.5f / (lambdaV + lambdaL); +} + +/** Evaluates the specular term of the BRDF based on the currently configured model. + + \param[in] sd Shading point data. + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] NdotH Dot product between shading normal and half vector, in positive hemisphere. + \param[in] LdotH Dot product between half vector and incident direction, in positive hemisphere. + \return f_r +*/ +float3 evalSpecular(const ShadingData sd, float NdotL, float NdotV, float NdotH, float LdotH) +{ + float alpha = max(epsilon_alpha_g, sd.ggxAlpha); // TODO: Derive appropriate epsilon + float D = evalNdfGGX(NdotH, alpha); +#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable + float G = evalMaskingSmithGGXSeparable(NdotL, NdotV, alpha); +#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated + float G = evalMaskingSmithGGXCorrelated(NdotL, NdotV, alpha); +#endif + float3 F = evalFresnelSchlick(sd.specular, 1, LdotH); + return D * G * F; // Note: G already includes 1/(4*NdotL*NdotV) factor. +} + +/** Evaluates the specular term of the BRDF based on the Disney's original model. + + The implementation matches exactly Disney's BRDF Explorer https://github.com/wdas/brdf, + (using its default values for all parameters we don't currently support). + + Note: Disney originally used a remapping alpha = pow(0.5 + 0.5*sd.linearRoughness, 2.f) for the masking-shadowing term (G) in their 2012 course notes. + This was later removed in their addendum and from the BRDF Explorer. The BRDF slice images in their course notes were rendered _with_ the remapping. + + \param[in] sd Shading point data. + \param[in] NdotL Dot product between shading normal and incident direction, in positive hemisphere. + \param[in] NdotV Dot product between shading normal and outgoing direction, in positive hemisphere. + \param[in] NdotH Dot product between shading normal and half vector, in positive hemisphere. + \param[in] LdotH Dot product between half vector and incident direction, in positive hemisphere. + \return f_r +*/ +float3 evalSpecularDisney(const ShadingData sd, float NdotL, float NdotV, float NdotH, float LdotH) +{ + float alpha = max(epsilon_alpha_g, sd.ggxAlpha); + float D = evalNdfGGX(NdotH, alpha); + float G = evalMaskingSmithGGXSeparable(NdotL, NdotV, alpha); + float3 F = evalFresnelSchlick(sd.specular, 1, LdotH); + return D * G * F; // Note: G already includes 1/(4*NdotL*NdotV) factor. +} + +/** Evaluates the BRDF for a given incident direction. + + \param[in] sd Shading point data. + \param[in] L Normalized incident direction from shading point towards light source. + \return f_d + f_r +*/ +float3 evalBRDF(const ShadingData sd, float3 L, const bool enableDiffuse = true, const bool enableSpecular = true) +{ + // Check that L and V are in the positive hemisphere. + // The G term on the correlated form is not robust for NdotL = NdotV = 0.0. + float NdotL = dot(sd.N, L); + if (min(sd.NdotV, NdotL) < kMinCosTheta) return float3(0, 0, 0); + + // Pre-compute half vector and dot products. + // TODO: Using saturate() here to be sure all dot products are within bounds. + // Some can be replaced by clamps on the upper end only (since we check NdotV and NdotL above) or removed altogether. + float3 H = normalize(sd.V + L); + float NdotH = saturate(dot(sd.N, H)); + float LdotH = saturate(dot(L, H)); + float NdotV = saturate(sd.NdotV); + NdotL = saturate(NdotL); + + // Evaluate diffuse and specular terms to compute total throughput. + float3 thp = enableDiffuse ? evalDiffuse(sd, NdotL, NdotV, LdotH) : float3(0); + if (enableSpecular) thp += evalSpecular(sd, NdotL, NdotV, NdotH, LdotH); + + return thp; +} + +/** Evaluates the BRDF for a given incident direction. + This version explicitly uses Disney's BRDF. + + \param[in] sd Shading point data. + \param[in] L Normalized incident direction from shading point towards light source. + \return f_d + f_r +*/ +float3 evalDisneyBRDF(const ShadingData sd, float3 L, const bool enableDiffuse = true, const bool enableSpecular = true) +{ + // Check that L and V are in the positive hemisphere. + // Everything below is robust for NdotL = NdotV = 0.0, as long as alpha is clamped to an epsilon. + // But leaving the check here to be consistent across different BRDF versions. + float NdotL = dot(sd.N, L); + if (min(sd.NdotV, NdotL) < kMinCosTheta) return float3(0, 0, 0); + + // Pre-compute half vector and dot products. + // TODO: Using saturate() here to be sure all dot products are within bounds. + // Some can be replaced by clamps on the upper end only (since we check NdotV and NdotL above) or removed altogether. + float3 H = normalize(sd.V + L); + float NdotH = saturate(dot(sd.N, H)); + float LdotH = saturate(dot(L, H)); + float NdotV = saturate(sd.NdotV); + NdotL = saturate(NdotL); + + // Evaluate diffuse and specular terms to compute total throughput. + float3 thp = enableDiffuse ? evalDiffuseDisney(sd, NdotL, NdotV, LdotH) : float3(0); + if (enableSpecular) thp += evalSpecularDisney(sd, NdotL, NdotV, NdotH, LdotH); + + // TODO: pbrt seems to include an extra multiplication by metallic * baseColor (or similar). + // That doesn't exist in Disney's BRDF explorer, which matches our implementation here exactly. + + return thp; +} + +/** Evaluates the BRDF multiplied by NdotL for a given incident direction. + + \param[in] sd Shading point data. + \param[in] L Normalized incident direction from shading point towards light source. + \return (f_d + f_r) * saturate(dot(N,L)) +*/ +float3 evalBRDFCosine(const ShadingData sd, float3 L, const bool enableDiffuse = true, const bool enableSpecular = true) +{ + return evalBRDF(sd, L, enableDiffuse, enableSpecular) * saturate(dot(sd.N, L)); +} + +/** Evaluates the BRDF multiplied by NdotL for a given incident direction. + This version explicitly uses Disney's BRDF. + + \param[in] sd Shading point data. + \param[in] L Normalized incident direction from shading point towards light source. + \return (f_d + f_r) * saturate(dot(N,L)) +*/ +float3 evalDisneyBRDFCosine(const ShadingData sd, float3 L, const bool enableDiffuse = true, const bool enableSpecular = true) +{ + return evalDisneyBRDF(sd, L, enableDiffuse, enableSpecular) * saturate(dot(sd.N, L)); +} + + +/******************************************************************* + BSDF sampling functions +*******************************************************************/ + +/** Describes a BRDF sample. + + Be careful not to use the returned pdf value unless necessary, as it can + be expensive to compute. It is often faster to check if thp != 0 to see + if a sample is valid, if the pdf is not otherwise needed. +*/ +struct BRDFSample +{ + float3 dir; ///< Sampling direction in world space (normalized). + float pdf; ///< Evaluated pdf with respect to solid angle from the shading point, or 0.0 if sample is invalid. + float3 thp; ///< Evaluated weight for the chosen direction (= f(V,L) * dot(N,L) / pdf). +}; + +/** Samples the cosine-weighted hemisphere at a hit point. + + \param[in] sd Describes the shading point. + \param[in] u Uniform random number (2D). + \param[out] pdf Sampling probability (= cos(theta) / pi). Note that pdf goes to zero at the horizon (relative to the shading normal). + \return Sampled direction in world space. +*/ +float3 sampleHemisphereCosine(ShadingData sd, float2 u, out float pdf) +{ + float3 dir = sample_cosine_hemisphere_concentric(u, pdf); + return fromLocal(dir, sd); +} + +/** Samples the GGX (Trowbridge-Reitz) normal distribution function (D) using Walter et al. 2007's method. + See Eqn 35 & 36 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf + See Listing A.1 in https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + + \param[in] sd Describes the shading point. + \param[in] u Uniform random number (2D). + \param[in] alpha GGX width parameter (should be clamped to small epsilon beforehand). + \param[out] pdf Sampling probability or 0.0 if sample is invalid (L is below the horizon). + \param[out] VdotH Byproduct of the sampling. + \param[out] NdotH Byproduct of the sampling. + \return Sampled direction in world space. +*/ +float3 sampleNdfGGX_Walter(const ShadingData sd, const float2 u, const float alpha, out float pdf, out float VdotH, out float NdotH) +{ + // Draw sample from D(H) * NdotH. + float a2 = alpha * alpha; + float cosThetaHSqr = min((1 - u.x) / ((a2 - 1) * u.x + 1), 1.0f); // Clamp to avoid 1.0+epsilon causing NaNs below. + float cosThetaH = sqrt(cosThetaHSqr); + float sinThetaH = sqrt(1 - cosThetaHSqr); + float phiH = u.y * M_2PI; + + // Convert half vector to world space. + float3 H = float3(sinThetaH * cos(phiH), sinThetaH * sin(phiH), cosThetaH); + H = fromLocal(H, sd); + + NdotH = cosThetaH; + VdotH = dot(sd.V, H); + + // Compute incident direction L by reflecting V about H. + float3 L = normalize(2.f * VdotH * H - sd.V); // Note: L is already of unit length, but normalize to reduce the risk of round-off errors. + + // Evaluate the pdf. + // The pdf in half vector space is pdf = D(H) * NdotH, which we multiply by the Jacobian of the half-vector transform. + float d = (a2 - 1) * cosThetaHSqr + 1; + pdf = (a2 * NdotH) / (d * d * VdotH * M_4PI); + //pdf = evalNdfGGX(NdotH, alpha) * NdotH / (4.f * VdotH); // For reference, identical to the line above + + // Reject sample if L is in the lower hemisphere. Note: We should check dot(N,V) elsewhere. + float NdotL = dot(sd.N, L); + if (NdotL < kMinCosTheta) + { + pdf = 0.f; + } + + return L; +} + +/** Evaluates the probability density function for the diffuse sampling strategy. + \param[in] sd Describes the shading point. + \param[in] L The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfDiffuse(const ShadingData sd, const float3 L) +{ + // We're using cosine-weighted sampling over the hemisphere. + float NdotL = dot(sd.N, L); + return NdotL < kMinCosTheta ? 0.f : NdotL * M_1_PI; +} + +/** Evaluates the probability density function for the Disney diffuse sampling strategy. + \param[in] sd Describes the shading point. + \param[in] L The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfDiffuseDisney(const ShadingData sd, const float3 L) +{ + return evalPdfDiffuse(sd, L); +} + +/** Importance sampling of the diffuse lobe of the BRDF times dot(N,L). + + \param[in] sd Describes the shading point. + \param[in] u Uniform random number (2D). + \param[out] result Generated sample with pre-evaluated weight (= f_d * dot(N,L) / pdf). +*/ +void sampleDiffuse(const ShadingData sd, const float2 u, out BRDFSample result) +{ + // Sample the diffuse lobe with pdf = NdotL / pi. + // The Disney diffuse is a Lambert times a Fresnel term to increase grazing retroreflection. The latter is not included in the pdf. + // TODO: Derive sampling method that better approminates the Disney diffuse lobe. + result.dir = sampleHemisphereCosine(sd, u, result.pdf); + + // Check that L and V are in the positive hemisphere. + float NdotL = dot(sd.N, result.dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) + { + result.pdf = 0.f; + result.thp = float3(0); + return; + } + + // Compute weight. Note that NdotL cancels out by the pdf. + result.thp = evalBRDF(sd, result.dir, true, false) * M_PI; +} + +/** Importance sampling of the diffuse lobe of Disney's BRDF times dot(N,L). + + \param[in] sd Describes the shading point. + \param[in] u Uniform random number (2D). + \param[out] result Generated sample with pre-evaluated weight (= f_d * dot(N,L) / pdf). +*/ +void sampleDiffuseDisney(const ShadingData sd, const float2 u, out BRDFSample result) +{ + // Sample the diffuse lobe with pdf = NdotL / pi. + // The Disney diffuse is a Lambert times a Fresnel term to increase grazing retroreflection. The latter is not included in the pdf. + // TODO: Derive sampling method that better approminates the Disney diffuse lobe. + result.dir = sampleHemisphereCosine(sd, u, result.pdf); + + // Check that L and V are in the positive hemisphere. + float NdotL = dot(sd.N, result.dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) + { + result.pdf = 0.f; + result.thp = float3(0); + return; + } + + // Compute weight. Note that NdotL cancels out by the pdf. + result.thp = evalDisneyBRDF(sd, result.dir, true, false) * M_PI; +} + +/** Evaluates the probability density function for the specular sampling strategy. + \param[in] sd Describes the shading point. + \param[in] L The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfSpecular(const ShadingData sd, const float3 L) +{ + // We're never generating samples for back-facing V or L. + float NdotL = dot(sd.N, L); + if (min(sd.NdotV, NdotL) < kMinCosTheta) + { + return 0.f; + } + + float3 H = normalize(sd.V + L); + float NdotH = saturate(dot(sd.N, H)); + float VdotH = saturate(dot(sd.V, H)); + + // We're sampling the GGX distribution with pdf = D(H) * NdotH / (4.f * VdotH). + float alpha = max(epsilon_alpha_g, sd.ggxAlpha); + return evalNdfGGX(NdotH, alpha) * NdotH / (4.f * VdotH); +} + +/** Evaluates the probability density function for the Disney specular sampling strategy. + \param[in] sd Describes the shading point. + \param[in] L The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfSpecularDisney(const ShadingData sd, const float3 L) +{ + return evalPdfSpecular(sd, L); +} + +/** Importance sampling the specular lobe of the BRDF. + + \param[in] sd Describes the shading point. + \param[in] u Uniform random number (2D). + \param[out] result Generated sample with pre-evaluated weight (= f_r * dot(N,L) / pdf). +*/ +void sampleSpecular(const ShadingData sd, const float2 u, out BRDFSample result) +{ + // Sample the GGX distribution with pdf = D(H) * NdotH / (4.f * VdotH). + float alpha = max(epsilon_alpha_g, sd.ggxAlpha); + float VdotH, NdotH; + result.dir = sampleNdfGGX_Walter(sd, u, alpha, result.pdf, VdotH, NdotH); + + // Check that L and V are in the positive hemisphere. + float NdotL = dot(sd.N, result.dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) + { + result.pdf = 0.f; + result.thp = float3(0); + return; + } + + // Pre-compute half vector and dot products. + // TODO: Look into necessary conditions for numerical robustness below. + float NdotV = saturate(sd.NdotV); + NdotL = saturate(NdotL); + NdotH = saturate(NdotH); + VdotH = saturate(VdotH); + + // Compute weight. Note that D cancels out by the pdf. +#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable + float G = evalMaskingSmithGGXSeparable(NdotL, NdotV, alpha); // Note: G already includes 1/(4*NdotL*NdotV) factor. +#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated + float G = evalMaskingSmithGGXCorrelated(NdotL, NdotV, alpha); +#endif + float3 F = evalFresnelSchlick(sd.specular, 1, VdotH); + result.thp = F * (G * NdotL * VdotH * 4.f) / NdotH; + //result.thp = evalBRDFCosine(sd, result.dir, false, true) / result.pdf; +} + +/** Importance sampling the specular lobe of Disney's BRDF. + + \param[in] sd Describes the shading point. + \param[in] u Uniform random number (2D). + \param[out] result Generated sample with pre-evaluated weight (= f_r * dot(N,L) / pdf). +*/ +void sampleSpecularDisney(const ShadingData sd, const float2 u, out BRDFSample result) +{ + // Sample the GGX distribution with pdf = D(H) * NdotH / (4.f * VdotH). + float alpha = max(epsilon_alpha_g, sd.ggxAlpha); + float VdotH, NdotH; + result.dir = sampleNdfGGX_Walter(sd, u, alpha, result.pdf, VdotH, NdotH); + + // Check that L and V are in the positive hemisphere. + float NdotL = dot(sd.N, result.dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) + { + result.pdf = 0.f; + result.thp = float3(0); + return; + } + + // Pre-compute half vector and dot products. + // TODO: Look into necessary conditions for numerical robustness below. + float NdotV = saturate(sd.NdotV); + NdotL = saturate(NdotL); + NdotH = saturate(NdotH); + VdotH = saturate(VdotH); + + // Compute weight. Note that D cancels out by the pdf. + // TODO: Make choice of masking-shadowing function configurable. + float G = evalMaskingSmithGGXSeparable(NdotL, NdotV, alpha); // Note: G already includes 1/(4*NdotL*NdotV) factor. + float3 F = evalFresnelSchlick(sd.specular, 1, VdotH); + result.thp = F * (G * NdotL * VdotH * 4.f) / NdotH; + //result.thp = evalDisneyBRDFCosine(sd, result.dir, false, true) / result.pdf; +} + +/** Evaluates the probability density function for both the diffuse and specular sampling strategy. + \param[in] sd Describes the shading point. + \param[in] L The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfBRDF(const ShadingData sd, const float3 L) +{ + float pDiffuse = 0.5f; // TODO: Better probabilities + + // Evaluate the pdf for the sample as a linear combination of the two sampling strategies' pdfs. + return pDiffuse * evalPdfDiffuse(sd, L) + (1.0f - pDiffuse) * evalPdfSpecular(sd, L); +} + +/** Evaluates the probability density function for both the Disney diffuse and specular sampling strategy. + \param[in] sd Describes the shading point. + \param[in] L The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfDisneyBRDF(const ShadingData sd, const float3 L) +{ + float pDiffuse = 0.5f; // TODO: Better probabilities + + // Evaluate the pdf for the sample as a linear combination of the two sampling strategies' pdfs. + return pDiffuse * evalPdfDiffuseDisney(sd, L) + (1.0f - pDiffuse) * evalPdfSpecularDisney(sd, L); +} + +/** Importance sampling of the BRDF. + + Note: The evaluated pdf for the generated sample is expensive to compute, as the pdf is a weighted + combination of two sampling strategies. If the caller doesn't explicitly need the probability, they + should be careful not to touch the value so that the compiler can do dead code elimination. + + \param[in] sd Shading point data. + \param[in] sg SampleGenerator. + \param[out] result Generated sample. +*/ +void sampleBRDF(const ShadingData sd, inout SampleGenerator sg, out BRDFSample result) +{ + // Draw uniform random numbers for lobe selection (1D) and sampling (2D). + const float2 u = sampleNext2D(sg); + const float uSelect = sampleNext1D(sg); + + float pDiffuse = 0.5f; // TODO: Better probabilities + + float pmfSelectedLobe; + float pdfOther; + + // Randomly select which lobe to sample. + if (uSelect < pDiffuse) + { + // Sample diffuse lobe. + sampleDiffuse(sd, u, result); + pmfSelectedLobe = pDiffuse; + + // Evaluate the pdf of the other sampling strategy. + pdfOther = evalPdfSpecular(sd, result.dir); + } + else + { + // Sample specular lobe. + sampleSpecular(sd, u, result); + pmfSelectedLobe = 1.f - pDiffuse; + + // Evaluate the pdf of the other sampling strategy. + pdfOther = evalPdfDiffuse(sd, result.dir); + } + + // Evaluate the pdf for the sample as a linear combination of the two sampling strategies' pdfs. + result.pdf = pmfSelectedLobe * result.pdf + (1.f - pmfSelectedLobe) * pdfOther; + + // Divide weight by the probability of the sampled lobe. + result.thp /= pmfSelectedLobe; +} + +/** Importance sampling of the Disney BRDF. + + Note: The evaluated pdf for the generated sample is expensive to compute, as the pdf is a weighted + combination of two sampling strategies. If the caller doesn't explicitly need the probability, they + should be careful not to touch the value so that the compiler can do dead code elimination. + + \param[in] sd Shading point data. + \param[in] sg SampleGenerator. + \param[out] result Generated sample. +*/ +void sampleDisneyBRDF(const ShadingData sd, inout SampleGenerator sg, out BRDFSample result) +{ + // Draw uniform random numbers for lobe selection (1D) and sampling (2D). + const float2 u = sampleNext2D(sg); + const float uSelect = sampleNext1D(sg); + + float pDiffuse = 0.5f; // TODO: Better probabilities + + float pmfSelectedLobe; + float pdfOther; + + // Randomly select which lobe to sample. + if (uSelect < pDiffuse) + { + // Sample diffuse lobe. + sampleDiffuseDisney(sd, u, result); + pmfSelectedLobe = pDiffuse; + + // Evaluate the pdf of the other sampling strategy. + pdfOther = evalPdfSpecularDisney(sd, result.dir); + } + else + { + // Sample specular lobe. + sampleSpecularDisney(sd, u, result); + pmfSelectedLobe = 1.f - pDiffuse; + + // Evaluate the pdf of the other sampling strategy. + pdfOther = evalPdfDiffuseDisney(sd, result.dir); + } + + // Evaluate the pdf for the sample as a linear combination of the two sampling strategies' pdfs. + result.pdf = pmfSelectedLobe * result.pdf + (1.f - pmfSelectedLobe) * pdfOther; + + // Divide weight by the probability of the sampled lobe. + result.thp /= pmfSelectedLobe; +} + + +// ---------------------------------------------------------------------- +// TODO: Validate all functions below!! +// The definitions of the BRDF evaluation/clamps/etc. above have changed. +// ---------------------------------------------------------------------- + +/** Result of material sampling. +*/ +struct MaterialSamplingResult +{ + float3 wi; ///< Incident direction after importance sampling the BRDF. + float pdf; ///< Probability density function for choosing the incident direction. + float3 thp; ///< Current path throughput. +}; + +/** Samples the diffuse lighting at a hit point. + \param[in] sd Describes the material properties and geometry at the hit point. + \param[in] u 2D random number + \param[out] msr MaterialSamplingResult struct with scattered direction and pdf +*/ +void sampleDiffuse(ShadingData sd, float2 u, inout MaterialSamplingResult msr) +{ + float3 L = sample_cosine_hemisphere_polar(u, msr.pdf); + L = fromLocal(L, sd); + msr.wi = L; + + // Eval diffuse lighting + // Compute half vector and dot products + float3 H = normalize(sd.V + L); // Note: The half vector H can be back-facing if V lies in the negative hemisphere + float NdotL = saturate(dot(sd.N, L)); + float NdotV = saturate(sd.NdotV); + float NdotH = saturate(dot(sd.N, H)); + float LdotH = saturate(dot(L, H)); + + msr.thp = evalDiffuse(sd, NdotL, NdotV, LdotH)* M_PI; +} + +/** Samples the lighting at a hit point. + \param[in] sd Describes the material properties and geometry at the hit point. + \param[in] u 2D random number + \param[out] msr MaterialSamplingResult struct with scattered direction and pdf +*/ +void sampleCosineBruteForce(ShadingData sd, float2 u, inout MaterialSamplingResult msr) +{ + float3 L = sample_cosine_hemisphere_polar(u, msr.pdf); + L = fromLocal(L, sd); + msr.wi = L; + + // Eval all lighting + // Compute half vector and dot products + float NdotL = saturate(dot(sd.N, L)); + msr.thp = evalBRDF(sd, L) * NdotL / msr.pdf; +} + +#if 0 +// DEPRECATED: Use evalPdfSpecularDisney() as replacement for now. + +/** Evaluates the probability density function for GGX ndf sampling. + \param[in] sd Describes the material properties and geometry at the hit point. + \param[in] L World space direction for which to evaluate the pdf. + \return The probability density for direction L. +*/ +float evalNdfGGXPdf(ShadingData sd, float3 L) +{ + // Early out if L or V lies in the negative hemisphere. The probability of such samples is zero. + float NdotL = dot(sd.N, L); + float NdotV = dot(sd.N, sd.V); + if (NdotL < epsilon || NdotV < epsilon) + { + return 0.f; + } + + float3 H = normalize(sd.V + L); + float NdotH = saturate(dot(sd.N, H)); + float LdotH = saturate(dot(L, H)); + + // Compute pdf. + // The pdf in half vector space is pdf = D(H) * |NdotH|, which is then divided by the Jacobian of the half-vector transform. + float pdf = evalNdfGGX(NdotH, sd.ggxAlpha) * NdotH; + pdf /= 4.f * LdotH; + + // Sanity check + if (LdotH < epsilon) + { + pdf = 0.f; + } + + return pdf; +} +#endif + +// /** Smith masking-shadowing function for the GGX normal distribution. +// See, e.g.: Eq 34 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf +// Only valid for cosTheta > 0 +float evalG1(float cosTheta, float ggxAlpha) +{ + if (cosTheta <= 0) return 0; + float a2 = ggxAlpha * ggxAlpha; + float cosTheta2 = cosTheta*cosTheta; + return 2.0 / (1.0f + sqrt(1.0f + a2*(1.0f - cosTheta2) / cosTheta2)); +} + +float evalG2(float NdotL, float NdotV, float ggxAlpha) +{ +#if SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXSeparable + float G = evalMaskingSmithGGXSeparable(NdotL, NdotV, ggxAlpha); +#elif SpecularMaskingFunction == SpecularMaskingFunctionSmithGGXCorrelated + float G = evalMaskingSmithGGXCorrelated(NdotL, NdotV, ggxAlpha); +#endif + return 4.0f * NdotL * NdotV * G; +} + +/** Samples the GGX lighting at a hit point using distribution of visible normals + according to https://hal.inria.fr/hal-00996995v2/document + Faster routine: https://hal.archives-ouvertes.fr/hal-01509746 + \param[in] sd Describes the material properties and geometry at the hit point. + \param[in] u 2D random number + \param[out] msr MaterialSamplingResult struct with scattered direction and pdf +*/ +void sampleGGX_Heitz(ShadingData sd, float2 u, float bias, inout MaterialSamplingResult msr) +{ + float alpha = sd.ggxAlpha * bias; + + float3 H = float3(0,0,1); + if (bias > 0.001f) + { + float3 V_local = toLocal(sd.V, sd); + + // Stretch + float3 V = normalize(float3(alpha * V_local.x, alpha * V_local.y, V_local.z)); + + // orthonormal basis + float3 T1 = (V.z < 0.9999) ? normalize(cross(V, float3(0, 0, 1))) : float3(1, 0, 0); + float3 T2 = cross(T1, V); + + // sample point with polar coordinates (r, phi) + float a = 1.0 / (1.0 + V.z); + float r = sqrt(u.x); + float phi = (u.y < a) ? u.y / a * M_PI : M_PI + (u.y - a) / (1.0 - a) * M_PI; + float P1 = r*cos(phi); + float P2 = r*sin(phi)*((u.y < a) ? 1.0 : V.z); + + // compute normal + H = P1*T1 + P2*T2 + sqrt(max(0.0, 1.0 - P1*P1 - P2*P2))*V; + + // unstretch + H = normalize(float3(alpha*H.x, alpha*H.y, max(0.0, H.z))); + } + H = fromLocal(H, sd); + + float3 L = normalize(2.f * dot(sd.V, H) * H - sd.V); // L sampled dir in world space + float NdotL = saturate(dot(L, sd.N)); + float NdotH = saturate(dot(H, sd.N)); + float LdotH = saturate(dot(H, L)); + msr.wi = L; // scattered direction + + // Sanity checks. Reject sample L if backfacing relative to N or H. + if (LdotH < epsilon || dot(sd.N, L) < epsilon || sd.NdotV < epsilon) + { + msr.pdf = 0.f; + msr.thp = float3(0, 0, 0); + return; + } + + // restore alpha without bias + alpha = sd.ggxAlpha; + + // Compute D_omega (distribution of visible normals) + // Equation 2 in https://hal.inria.fr/hal-00996995v2/document + // In that formula w_i : Direction from which light is incident, in our case: V + // w_o : Direction in which light is scattered, in our case: L + + float D = evalNdfGGX(NdotH, alpha); + float G1 = evalG1(sd.NdotV, alpha); + float G2 = evalG2(NdotL, sd.NdotV, alpha); + + // Compute pdf. + msr.pdf = G1 * D * 0.25f / sd.NdotV; + + // Compute weight + float3 F = evalFresnelSchlick(sd.specular, 1, max(0, LdotH)); + msr.thp = F * G2 / G1; +} + +/** Samples the GGX lighting at a hit point according to. + https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf + \param[in] sd Describes the material properties and geometry at the hit point. + \param[in] u 2D random number + \param[out] msr MaterialSamplingResult struct with scattered direction and pdf +*/ +void sampleGGX_Walter(ShadingData sd, float2 u, inout MaterialSamplingResult msr) +{ + // GGX NDF sampling + float a2 = sd.ggxAlpha * sd.ggxAlpha; + float cosThetaH = sqrt((1 - u.x) / ((a2 - 1) * u.x + 1)); + float sinThetaH = sqrt(1 - cosThetaH * cosThetaH); + //float sinThetaH = sqrt(max(0, 1 - cosThetaH * cosThetaH)); // TODO: Check if clamp is ever needed due to numerical imprecision (example code from Frostbite had it). Should not be. + float phiH = u.y * M_PI * 2; + + // Convert sample from half angle to incident angle + float3 H = float3(sinThetaH * cos(phiH), sinThetaH * sin(phiH), cosThetaH); // H in local frame + + H = fromLocal(H, sd); + + float3 L = normalize(2.f * dot(sd.V, H) * H - sd.V); // L sampled dir in world space + + float NdotH = saturate(dot(H, sd.N)); + float NdotL = saturate(dot(L, sd.N)); + float LdotH = saturate(dot(H, L)); + + // Compute pdf. + // The pdf in half vector space is pdf = D(H) * |NdotH|, which is then divided by the Jacobian of the half-vector transform. + msr.pdf = evalNdfGGX(NdotH, sd.ggxAlpha) * NdotH * 0.25f / LdotH; + msr.wi = L; // scattered direction + + // Sanity checks. Reject sample L if backfacing relative to N or H. + if (LdotH < epsilon || dot(sd.N, L) < epsilon) + { + msr.pdf = 0.f; + msr.thp = float3(0, 0, 0); + return; + } + + // Compute weight + float3 F = evalFresnelSchlick(sd.specular, 1, max(0, LdotH)); + float G2 = evalG2(NdotL, sd.NdotV, sd.ggxAlpha); + msr.thp = F * LdotH * G2 / (sd.NdotV * NdotH); // sample weight +} + + +/** Helper function to compute the probability of sampling the diffuse lobe at a hit point. +*/ +float getDiffusePmf(ShadingData sd) +{ + // The specular component can be non-zero even if specular albedo is zero, as the fresnel term goes to 1 at grazing angles. + // We handle the case of diff+spec < epsilon by always sampling the specular component in that case. + float diffAlbedo = luminance(sd.diffuse); + float specAlbedo = luminance(sd.specular); + float pmf = (diffAlbedo + specAlbedo) < epsilon ? 0.f : diffAlbedo / (diffAlbedo + specAlbedo); +#ifdef DisableSpecularSampling + pmf = 1.f; +#endif + return pmf; +} + +/** Evaluates the probability density function used for material sampling. + This is useful for MIS, where we need to evaluate the probability for samples generated using other techniques. + \param[in] sd Describes the material properties and geometry at the hit point. + \param[in] L World space direction for which to evaluate the pdf. + \return The probability density for direction L. +*/ +float evalMaterialPdf(ShadingData sd, float3 L) +{ + // Samples in the negative hemisphere have zero probability. + float NdotL = dot(sd.N, L); + if (NdotL < epsilon) + { + return 0.f; + } + + float pdfDiffuse = NdotL * M_1_PI; + + //float pdfSpecular = evalNdfGGXPdf(sd, L); // Old function + float pdfSpecular = evalPdfSpecularDisney(sd, L); + + // The final pdf is a blend between the diffuse and specular pdf:s as the sample function stochastically chooses between them. + float pmf = getDiffusePmf(sd); + return pmf * pdfDiffuse + (1.f - pmf) * pdfSpecular; +} + + +/** Importance sampling of the material. + This takes both the diffuse and specular parts into account. +*/ +void sampleMaterial(ShadingData sd, inout SampleGenerator sg, inout MaterialSamplingResult result, float specularLobeBias=0.0f) +{ + float3 sample = sampleNext3D(sg); + + // sampleCosineBruteForce(sd, sample.xy, result); // For verification purposes + +#if 1 + float pmf = getDiffusePmf(sd); // Probability of sampling the diffuse component + if (sample.z < pmf) + { + sampleDiffuse(sd, sample.xy, result); // Sample diffuse + result.pdf /= pmf; // re-weigh the contributions + result.thp /= pmf; + } + else + { + // Sample specular + float2 u = sample.xy; + u.x = lerp(0.0, sample.x, saturate(1.0 - specularLobeBias)); // Hack to avoid sampling the tail of the GGX distrib + + // Old sampling functions. + //sampleGGX_Heitz(sd, sample, saturate(1.0 - specularLobeBias), result); // Sample specular + //sampleGGX_Walter(sd, u, result); // Sample specular + + // Use new sampling function. + // TODO: This clamps dot products etc. differently, so we should update all code. This is just a temporary fix. + BRDFSample s; + sampleSpecularDisney(sd, u, s); + result.wi = s.dir; + result.thp = s.thp; + result.pdf = s.pdf; + + + float pmf_spec = saturate(1.0 - pmf); // Probability of sampling the specular component + result.pdf /= pmf_spec; // re-weigh the contributions + result.thp /= pmf_spec; + } +#endif +} + +#undef epsilon diff --git a/Source/Falcor/Falcor.h b/Source/Falcor/Falcor.h new file mode 100644 index 000000000..7fedab272 --- /dev/null +++ b/Source/Falcor/Falcor.h @@ -0,0 +1,183 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include "Core/Framework.h" +#define _USE_MATH_DEFINES +#include + +// Core +#include "Core/Sample.h" +#include "Core/Window.h" + +// Core/API +#include "Core/API/BlendState.h" +#include "Core/API/Buffer.h" +#include "Core/API/ComputeContext.h" +#include "Core/API/ComputeStateObject.h" +#include "Core/API/CopyContext.h" +#include "Core/API/DepthStencilState.h" +#include "Core/API/DescriptorPool.h" +#include "Core/API/DescriptorSet.h" +#include "Core/API/Device.h" +#include "Core/API/FBO.h" +#include "Core/API/FencedPool.h" +#include "Core/API/Formats.h" +#include "Core/API/GpuFence.h" +#include "Core/API/GpuTimer.h" +#include "Core/API/GraphicsStateObject.h" +#include "Core/API/LowLevelContextData.h" +#include "Core/API/QueryHeap.h" +#include "Core/API/RasterizerState.h" +#include "Core/API/RenderContext.h" +#include "Core/API/Resource.h" +#include "Core/API/GpuMemoryHeap.h" +#include "Core/API/ResourceViews.h" +#include "Core/API/RootSignature.h" +#include "Core/API/Sampler.h" +#include "Core/API/Texture.h" +#include "Core/API/VAO.h" +#include "Core/API/VertexLayout.h" + +// Core/BufferTypes +#include "Core/BufferTypes/ConstantBuffer.h" +#include "Core/BufferTypes/StructuredBuffer.h" +#include "Core/BufferTypes/TypedBuffer.h" +#include "Core/BufferTypes/VariablesBuffer.h" +#include "Core/BufferTypes/VariablesBufferUI.h" + +// Core/Platform +#include "Core/Platform/OS.h" +#include "Core/Platform/ProgressBar.h" + +// Core/Program +#include "Core/Program/ComputeProgram.h" +#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/ParameterBlock.h" +#include "Core/Program/Program.h" +#include "Core/Program/ProgramReflection.h" +#include "Core/Program/ProgramVars.h" +#include "Core/Program/ProgramVersion.h" +#include "Core/Program/ShaderLibrary.h" + +// Core/State +#include "Core/State/ComputeState.h" +#include "Core/State/GraphicsState.h" + +// Effects +#include "Effects/AmbientOcclusion/SSAOPass.h" +#include "Effects/FXAA/FXAAPass.h" +#include "Effects/Shadows/CSM.h" +#include "Effects/SkyBox/SkyBox.h" +#include "Effects/TAA/TAAPass.h" +#include "Effects/ToneMapping/ToneMappingPass.h" +#include "Effects/Utils/GaussianBlurPass.h" + +// RenderGraph +#include "RenderGraph/RenderGraph.h" +#include "RenderGraph/RenderGraphImportExport.h" +#include "RenderGraph/RenderGraphIR.h" +#include "RenderGraph/RenderGraphUI.h" +#include "RenderGraph/RenderPass.h" +#include "RenderGraph/RenderPassLibrary.h" +#include "RenderGraph/RenderPassReflection.h" +#include "RenderGraph/RenderPassStandardFlags.h" +#include "RenderGraph/ResourceCache.h" +#include "RenderGraph/BasePasses/ComputePass.h" +#include "RenderGraph/BasePasses/RasterPass.h" +#include "RenderGraph/BasePasses/RasterScenePass.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" + +// Scene +#include "Scene/Scene.h" +#include "Scene/Importers/SceneImporter.h" +#include "Scene/Camera/Camera.h" +#include "Scene/Camera/CameraController.h" +#include "Scene/Lights/Light.h" +#include "Scene/Lights/LightProbe.h" +#include "Scene/Material/Material.h" +#include "Scene/Animation/Animation.h" +#include "Scene/Animation/AnimationController.h" +#include "Scene/Importers/AssimpImporter.h" +#include "Scene/ParticleSystem/ParticleSystem.h" + +// Utils +#include "Utils/Math/AABB.h" +#include "Utils/ArgList.h" +#include "Utils/BinaryFileStream.h" +#include "Utils/Logger.h" +#include "Utils/StringUtils.h" +#include "Utils/Threading.h" +#include "Utils/Algorithm/DirectedGraph.h" +#include "Utils/Algorithm/DirectedGraphTraversal.h" +#include "Utils/Algorithm/ParallelReduction.h" +#include "Utils/Image/Bitmap.h" +#include "Utils/Math/CubicSpline.h" +#include "Utils/Math/FalcorMath.h" +#include "Utils/Scripting/Dictionary.h" +#include "Utils/Perception/Experiment.h" +#include "Utils/Perception/SingleThresholdMeasurement.h" +#include "Utils/SampleGenerators/DxSamplePattern.h" +#include "Utils/SampleGenerators/HaltonSamplePattern.h" +#include "Utils/SampleGenerators/StratifiedSamplePattern.h" +#include "Utils/SampleGenerators/CPUSampleGenerator.h" +#include "Utils/Scripting/Scripting.h" +#include "Utils/Scripting/Console.h" +#include "Utils/Timing/CpuTimer.h" +#include "Utils/Timing/Clock.h" +#include "Utils/Timing/FrameRate.h" +#include "Utils/Timing/Profiler.h" +#include "Utils/UI/Font.h" +#include "Utils/UI/Gizmo.h" +#include "Utils/UI/Gui.h" +#include "Utils/UI/DebugDrawer.h" +#include "Utils/UI/Picking.h" +#include "Utils/UI/PixelZoom.h" +#include "Utils/UI/TextRenderer.h" +#include "Utils/UI/UserInput.h" +#include "Utils/Video/VideoEncoder.h" +#include "Utils/Video/VideoEncoderUI.h" +#include "Utils/Debug/DebugConsole.h" +#include "Utils/Debug/PixelDebug.h" + +#ifdef FALCOR_D3D12 +#include "Raytracing/RtProgramVars.h" +#include "Raytracing/RtState.h" +#include "Raytracing/RtProgram/RtProgram.h" +#endif + +#define FALCOR_MAJOR_VERSION 4 +#define FALCOR_MINOR_VERSION 0 +#define FALCOR_DEV_STAGE "preview" +#define FALCOR_DEV_REVISION 0 +#define FALCOR_VERSION_STRING "4.0preview.0" diff --git a/Framework/Source/Falcor.natvis b/Source/Falcor/Falcor.natvis similarity index 100% rename from Framework/Source/Falcor.natvis rename to Source/Falcor/Falcor.natvis diff --git a/Source/Falcor/Falcor.props b/Source/Falcor/Falcor.props new file mode 100644 index 000000000..55f9a5b41 --- /dev/null +++ b/Source/Falcor/Falcor.props @@ -0,0 +1,47 @@ + + + + + $(SolutionDir)\Source + FALCOR_D3D12 + + + $(SolutionDir)Bin\$(PlatformShortName)\$(Configuration)\ + $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\$(ProjectName)\ + + + + Level3 + true + $(FALCOR_CORE_DIRECTORY)\Externals\.packman\DL4RT\Include;$(FALCOR_CORE_DIRECTORY)\Falcor;$(FALCOR_CORE_DIRECTORY)\Internal;$(FALCOR_CORE_DIRECTORY)\Internal\RenderPassCommon;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\nvapi;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\GLM;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\VulkanSDK\Include;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\RapidJson\include;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\pybind11\include;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\Python\include;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\WinPixEventRuntime\Include\WinPixEventRuntime;$(FALCOR_CORE_DIRECTORY)\Externals;$(FALCOR_CORE_DIRECTORY)\Externals\.packman + _$(OutputType);_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;GLM_FORCE_DEPTH_ZERO_TO_ONE;$(FALCOR_BACKEND);_UNICODE;UNICODE;%(PreprocessorDefinitions) + stdcpp17 + + + $(FALCOR_CORE_DIRECTORY)\Externals\.packman\DL4RT\Lib\x64\$(ConfigurationName)\;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\FreeImage;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\Assimp\lib\$(PlatformName)\;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\FFMpeg\lib\$(PlatformName);$(FALCOR_CORE_DIRECTORY)\Externals\.packman\openvr\lib\win64;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\nvapi\amd64;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\VulkanSDK\Lib;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\Slang\bin\windows-x64\release;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\GLFW\lib;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\Python\libs;$(FALCOR_CORE_DIRECTORY)\Externals\.packman\WinPixEventRuntime\bin\x64 + WinPixEventRuntime.lib;glfw3dll.lib;slang.lib;Comctl32.lib;Shlwapi.lib;assimp.lib;freeimage.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;avcodec.lib;avutil.lib;avformat.lib;swscale.lib;Shcore.lib;%(AdditionalDependencies) + + + call $(FALCOR_CORE_DIRECTORY)\..\Build\deployproject.bat $(ProjectDir) $(OutDir) + + + + + Project + + + + + + + + Data + true + + + + $(FALCOR_CORE_DIRECTORY) + true + + + \ No newline at end of file diff --git a/Source/Falcor/Falcor.vcxproj b/Source/Falcor/Falcor.vcxproj new file mode 100644 index 000000000..b6836f001 --- /dev/null +++ b/Source/Falcor/Falcor.vcxproj @@ -0,0 +1,1155 @@ + + + + + DebugD3D12 + x64 + + + DebugVK + x64 + + + ReleaseD3D12 + x64 + + + ReleaseVK + x64 + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + + + + + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + + + + + + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + + + true + true + true + true + + + + + true + true + true + true + + + + + + + true + + + {2C535635-E4C5-4098-A928-574F0E7CD5F9} + Win32Proj + Falcor + 10.0.17763.0 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + Unicode + true + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + $(SolutionDir)Bin\$(PlatformShortName)\Debug\ + $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ + Clean + + + $(SolutionDir)Bin\$(PlatformShortName)\Debug\ + $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ + Clean + + + $(SolutionDir)Bin\$(PlatformShortName)\Release\ + $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ + Clean + + + $(SolutionDir)Bin\$(PlatformShortName)\Release\ + $(SolutionDir)Bin\Int\$(PlatformShortName)\$(Configuration)\ + Clean + + + + Level3 + Disabled + FALCOR_DLL;IMGUI_API=__declspec(dllexport);_PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;FALCOR_D3D12;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE;_$(OutputType) + $(ProjectDir);$(ProjectDir)\..\Externals\.packman\GLM;$(ProjectDir)\..\Externals\.packman\GLFW\include;$(ProjectDir)\..\Externals\.packman\FreeImage;$(ProjectDir)\..\Externals\.packman\ASSIMP\include;$(ProjectDir)\..\Externals\.packman\FFMpeg\include;$(ProjectDir)\..\Externals\.packman\\OpenVR\headers;$(ProjectDir)\..\Externals\.packman\RapidJson\include;$(ProjectDir)\..\Externals\.packman\VulkanSDK\Include;$(ProjectDir)\..\Externals\.packman\Python\Include;$(ProjectDir)\..\Externals\.packman\pybind11\include;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\.packman\nvapi;$(ProjectDir)\..\Externals\.packman + true + true + stdcpp17 + true + Use + /bigobj + + + Windows + true + $(ProjectDir)..\Externals\.packman\FreeImage;$(ProjectDir)..\Externals\.packman\Assimp\lib\$(PlatformName)\;$(ProjectDir)..\Externals\.packman\FFMpeg\lib\$(PlatformName);$(ProjectDir)..\Externals\.packman\openvr\lib\win64;$(ProjectDir)..\Externals\.packman\nvapi\amd64;$(ProjectDir)..\Externals\.packman\VulkanSDK\Lib;$(ProjectDir)..\Externals\.packman\Slang\bin\windows-x64\release;$(ProjectDir)..\Externals\.packman\GLFW\lib;$(ProjectDir)..\Externals\.packman\Python\libs;$(ProjectDir)..\Externals\.packman\WinPixEventRuntime\bin\x64 + WinPixEventRuntime.lib;glfw3dll.lib;slang.lib;Comctl32.lib;Shlwapi.lib;assimp.lib;freeimage.lib;avcodec.lib;avutil.lib;avformat.lib;swscale.lib;Shcore.lib;%(AdditionalDependencies) + + + call $(ProjectDir)\..\..\Build\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) + + + + + + + + + + + + + + + call $(ProjectDir)\..\..\Build\deploycommon.bat $(ProjectDir)\.. $(PlatformShortName) $(OutDir) $(FALCOR_BACKEND) "$(WindowsSdkDir)" + + + + + Level3 + Disabled + FALCOR_DLL;IMGUI_API=__declspec(dllexport);_PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;FALCOR_VK;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE + $(ProjectDir);$(ProjectDir)\..\Externals\.packman\GLM;$(ProjectDir)\..\Externals\.packman\GLFW\include;$(ProjectDir)\..\Externals\.packman\FreeImage;$(ProjectDir)\..\Externals\.packman\ASSIMP\include;$(ProjectDir)\..\Externals\.packman\FFMpeg\include;$(ProjectDir)\..\Externals\.packman\\OpenVR\headers;$(ProjectDir)\..\Externals\.packman\RapidJson\include;$(ProjectDir)\..\Externals\.packman\VulkanSDK\Include;$(ProjectDir)\..\Externals\.packman\Python\Include;$(ProjectDir)\..\Externals\.packman\pybind11\include;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\.packman\nvapi;$(ProjectDir)\..\Externals\.packman + true + true + stdcpp17 + true + Use + /bigobj + + + Windows + true + $(ProjectDir)..\Externals\.packman\FreeImage;$(ProjectDir)..\Externals\.packman\Assimp\lib\$(PlatformName)\;$(ProjectDir)..\Externals\.packman\FFMpeg\lib\$(PlatformName);$(ProjectDir)..\Externals\.packman\openvr\lib\win64;$(ProjectDir)..\Externals\.packman\nvapi\amd64;$(ProjectDir)..\Externals\.packman\VulkanSDK\Lib;$(ProjectDir)..\Externals\.packman\Slang\bin\windows-x64\release;$(ProjectDir)..\Externals\.packman\GLFW\lib;$(ProjectDir)..\Externals\.packman\Python\libs;$(ProjectDir)..\Externals\.packman\WinPixEventRuntime\bin\x64 + WinPixEventRuntime.lib;glfw3dll.lib;slang.lib;Comctl32.lib;Shlwapi.lib;assimp.lib;freeimage.lib;avcodec.lib;avutil.lib;avformat.lib;swscale.lib;Shcore.lib;%(AdditionalDependencies) + + + call $(ProjectDir)\..\..\Build\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) + + + + + + + + + + + + + + + + + call $(ProjectDir)\..\..\Build\deploycommon.bat $(ProjectDir)\.. $(PlatformShortName) $(OutDir) $(FALCOR_BACKEND) "$(WindowsSdkDir)" + + + + + Level3 + MaxSpeed + true + FALCOR_DLL;IMGUI_API=__declspec(dllexport);_PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;FALCOR_D3D12;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE + $(ProjectDir);$(ProjectDir)\..\Externals\.packman\GLM;$(ProjectDir)\..\Externals\.packman\GLFW\include;$(ProjectDir)\..\Externals\.packman\FreeImage;$(ProjectDir)\..\Externals\.packman\ASSIMP\include;$(ProjectDir)\..\Externals\.packman\FFMpeg\include;$(ProjectDir)\..\Externals\.packman\\OpenVR\headers;$(ProjectDir)\..\Externals\.packman\RapidJson\include;$(ProjectDir)\..\Externals\.packman\VulkanSDK\Include;$(ProjectDir)\..\Externals\.packman\Python\Include;$(ProjectDir)\..\Externals\.packman\pybind11\include;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\.packman\nvapi;$(ProjectDir)\..\Externals\.packman + true + true + stdcpp17 + true + Use + /bigobj + + + Windows + true + true + true + $(ProjectDir)..\Externals\.packman\FreeImage;$(ProjectDir)..\Externals\.packman\Assimp\lib\$(PlatformName)\;$(ProjectDir)..\Externals\.packman\FFMpeg\lib\$(PlatformName);$(ProjectDir)..\Externals\.packman\openvr\lib\win64;$(ProjectDir)..\Externals\.packman\nvapi\amd64;$(ProjectDir)..\Externals\.packman\VulkanSDK\Lib;$(ProjectDir)..\Externals\.packman\Slang\bin\windows-x64\release;$(ProjectDir)..\Externals\.packman\GLFW\lib;$(ProjectDir)..\Externals\.packman\Python\libs;$(ProjectDir)..\Externals\.packman\WinPixEventRuntime\bin\x64 + WinPixEventRuntime.lib;glfw3dll.lib;slang.lib;Comctl32.lib;Shlwapi.lib;assimp.lib;freeimage.lib;avcodec.lib;avutil.lib;avformat.lib;swscale.lib;Shcore.lib;%(AdditionalDependencies) + + + call $(ProjectDir)\..\..\Build\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) + + + + + + + + + + + %(AdditionalLibraryDirectories) + + + + + call $(ProjectDir)\..\..\Build\deploycommon.bat $(ProjectDir)\.. $(PlatformShortName) $(OutDir) $(FALCOR_BACKEND) "$(WindowsSdkDir)" + + + + + Level3 + MaxSpeed + true + FALCOR_DLL;IMGUI_API=__declspec(dllexport);_PROJECT_DIR_=R"($(ProjectDir))";_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;FALCOR_VK;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GLM_FORCE_DEPTH_ZERO_TO_ONE + $(ProjectDir);$(ProjectDir)\..\Externals\.packman\GLM;$(ProjectDir)\..\Externals\.packman\GLFW\include;$(ProjectDir)\..\Externals\.packman\FreeImage;$(ProjectDir)\..\Externals\.packman\ASSIMP\include;$(ProjectDir)\..\Externals\.packman\FFMpeg\include;$(ProjectDir)\..\Externals\.packman\\OpenVR\headers;$(ProjectDir)\..\Externals\.packman\RapidJson\include;$(ProjectDir)\..\Externals\.packman\VulkanSDK\Include;$(ProjectDir)\..\Externals\.packman\Python\Include;$(ProjectDir)\..\Externals\.packman\pybind11\include;$(ProjectDir)\..\Externals\;$(ProjectDir)\..\Externals\.packman\nvapi;$(ProjectDir)\..\Externals\.packman + true + true + stdcpp17 + true + Use + /bigobj + + + Windows + true + true + true + $(ProjectDir)..\Externals\.packman\FreeImage;$(ProjectDir)..\Externals\.packman\Assimp\lib\$(PlatformName)\;$(ProjectDir)..\Externals\.packman\FFMpeg\lib\$(PlatformName);$(ProjectDir)..\Externals\.packman\openvr\lib\win64;$(ProjectDir)..\Externals\.packman\nvapi\amd64;$(ProjectDir)..\Externals\.packman\VulkanSDK\Lib;$(ProjectDir)..\Externals\.packman\Slang\bin\windows-x64\release;$(ProjectDir)..\Externals\.packman\GLFW\lib;$(ProjectDir)..\Externals\.packman\Python\libs;$(ProjectDir)..\Externals\.packman\WinPixEventRuntime\bin\x64 + WinPixEventRuntime.lib;glfw3dll.lib;slang.lib;Comctl32.lib;Shlwapi.lib;assimp.lib;freeimage.lib;avcodec.lib;avutil.lib;avformat.lib;swscale.lib;Shcore.lib;%(AdditionalDependencies) + + + call $(ProjectDir)\..\..\Build\prebuild.bat $(ProjectDir)\..\ $(SolutionDir) $(ProjectDir) $(PlatformName) $(PlatformShortName) $(Configuration) $(OutDir) + + + + + + + + + + + + + + + + + call $(ProjectDir)\..\..\Build\deploycommon.bat $(ProjectDir)\.. $(PlatformShortName) $(OutDir) $(FALCOR_BACKEND) "$(WindowsSdkDir)" + + + + + + + + Project + + + + + + + + Data + true + + \ No newline at end of file diff --git a/Source/Falcor/Falcor.vcxproj.filters b/Source/Falcor/Falcor.vcxproj.filters new file mode 100644 index 000000000..b430a5467 --- /dev/null +++ b/Source/Falcor/Falcor.vcxproj.filters @@ -0,0 +1,1575 @@ + + + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\State + + + Core\State + + + Core + + + Core + + + Core + + + Data\Effects + + + Data\Effects + + + Data + + + Data + + + Data + + + Data + + + Effects\Shadows + + + Effects\SkyBox + + + Scene\Camera + + + Scene\Camera + + + Scene\Lights + + + Scene\Lights + + + Scene\Material + + + Testing + + + Utils + + + Utils + + + Utils + + + Utils\Scripting + + + Utils\Video + + + Utils\Video + + + + + Utils\Timing + + + Utils\Timing + + + Utils\Timing + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\Image + + + Utils\Image + + + Utils\Image + + + Utils\Algorithm + + + Core\Platform + + + Core\Platform + + + Core\BufferTypes + + + Core\BufferTypes + + + Core\BufferTypes + + + Core\BufferTypes + + + Core\BufferTypes + + + Utils\Perception + + + Utils\Perception + + + Data\Effects + + + + Core + + + Core + + + Utils\UI + + + Core\Platform + + + Utils\Scripting + + + Core\State + + + Utils\Algorithm + + + Utils\Algorithm + + + Utils\SampleGenerators + + + Utils\SampleGenerators + + + Utils\SampleGenerators + + + Core\Program + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui_addons + + + Data\Effects + + + Utils\Scripting + + + Utils + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + Utils + + + Utils\Timing + + + Utils\Scripting + + + Utils\Color + + + Core\Program + + + Core\API + + + Scene + + + Externals\mikktspace + + + Data\Effects + + + Utils\SampleGenerators + + + Scene + + + Effects\AmbientOcclusion + + + Effects\FXAA + + + Effects\TAA + + + Effects\ToneMapping + + + Scene\ParticleSystem + + + Effects\Utils + + + Scene\Animation + + + Scene\Animation + + + Scene\Importers + + + Scene\Importers + + + RenderPasses + + + RenderPasses + + + RenderPasses + + + RenderPasses + + + RenderPasses + + + Raytracing\RtProgram + + + Raytracing\RtProgram + + + Raytracing\RtProgram + + + Raytracing\RtProgram + + + Raytracing + + + Raytracing + + + Raytracing + + + Raytracing + + + Raytracing + + + Raytracing + + + RenderGraph + + + Utils\Debug + + + Utils\Debug + + + Utils\Algorithm + + + Utils\Algorithm + + + Utils\Algorithm + + + Utils\Math + + + Utils\Math + + + Utils\Math + + + Utils\Math + + + Utils\Math + + + Utils\Sampling + + + Utils + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + + + {fc024fe5-7aa0-45ec-95d3-cf8a36a0604c} + + + {1d9e2adf-2ce8-4026-a082-0a4009876488} + + + {c95f5d9c-1636-49d4-998f-495bade41a7e} + + + {9bc470ff-b97f-48f8-9841-6aed7b07e822} + + + {6a7f5301-b62c-42de-a2af-6acc0448d998} + + + {ec387bf8-0d4e-48d0-846a-305ce558b727} + + + {115a34a9-2ed6-4f77-a58f-810cb49b6085} + + + {f2bed833-8f77-4eb5-b0f1-f18c893bdc08} + + + {3cdc3f41-fcfd-42dd-bd4b-f1c9d5c67304} + + + {8c84c20f-e37e-4c69-b7e8-731eb016fb51} + + + {2ff93073-2a76-4095-9457-fbcbe02604bb} + + + {890cabee-2264-4649-b20e-67dcdd66901d} + + + {188c8e95-ac0b-42b4-aca4-6975d72815fe} + + + {a4498547-fcca-476f-a3f3-46c4237266bd} + + + {3b2bb526-6078-4582-aa31-13e0264c8390} + + + {31a70a24-36ea-4646-a1a8-67c9641c5136} + + + {0f5d3a56-5f16-430d-80cc-3d366491a446} + + + {0dc706de-443b-4e04-afca-59f2873260b2} + + + {8c79ad18-c10d-42d3-b5b3-043121666e60} + + + {7faca135-6754-4385-ac63-19b3cc66645c} + + + {b2a28c0d-a5c4-4009-b6f5-b78ab7c332f0} + + + {3655e9d0-9f32-45d9-b2c2-4ab1b6d6c2cd} + + + {8c8e9d50-7602-4d7e-85c7-26a0a81384be} + + + {def6a499-c8b2-4ae8-9922-420f6238b6d8} + + + {6dec3763-1b49-4566-a58e-1525e77c65e1} + + + {e4e519f9-020d-44b0-8870-b8367286dc00} + + + {118b7402-d149-48b8-804d-5fcb4205ef72} + + + {3e0ab2ec-f2c4-41d5-b10d-60f70ebc2d6d} + + + {9999c231-948d-4160-a088-12b25bef6a3b} + + + {3a04af1b-9d6e-42f2-b2a6-d6b4b2922aeb} + + + {06f3304e-12ad-420b-9cef-b460bd61dc0d} + + + {d33ca7a1-7b44-44c8-b36e-7e5e6b77e68a} + + + {7fc1faaf-4fa0-4a81-ae01-56a753e90df5} + + + {7772a30e-d0e2-4f31-a15d-a216bc35fb3c} + + + {be2db7dc-a056-481f-b4a7-2c2200f24c84} + + + {4e4c07fc-b797-4aa3-825d-9c2be22f8779} + + + {76f8f596-3289-4454-a85b-e4a64ffe9c7b} + + + {9d2e420c-c138-442a-92f8-6708bccd83b5} + + + {247c1c5d-bde2-4022-a1a6-94e708cf7b4f} + + + {7e48640c-bc9b-4c5b-9d85-07742285d7f0} + + + {a6798266-6b9d-4dc4-8192-ee74ec5ff190} + + + {a7eea0a2-3bb5-4920-a0c2-542e81f02cad} + + + {03a384b8-3d4f-4e76-973e-1e215d71603d} + + + {e8da297a-6e6a-4104-8948-53a5e0c67c29} + + + {13ab0eff-5708-4b2c-9ec3-d6d1df839719} + + + {a5e366f9-af02-4a41-a630-621da069c661} + + + {32845222-2924-47c4-8b2e-43bf44e7a85a} + + + {6d9bd53c-0171-4f34-80ed-77d948789c2b} + + + {2639b21f-a7da-42dc-80eb-6445f1429668} + + + {a404d3b4-db30-4cbb-b4c0-14914c2e632e} + + + {017d4e86-3442-47ea-972e-14c11ab4fa54} + + + {d0bf1d70-9550-409c-b072-70bd43e64d67} + + + {001bb8ce-735b-44d4-b170-0140b510dd0e} + + + {71cc7354-bbb3-4fda-ab6d-bf93a3e05d8a} + + + {bf53cf51-a063-4004-b562-ad4d1c68879a} + + + {0ee0f6df-2831-4da1-bdb5-d910ae1a6126} + + + {1936fdab-bbc5-4d52-bd4f-fe6346e8ee4c} + + + {e73a5a47-f7ca-4606-a4f9-8395d8758080} + + + {f68ff384-c7a3-48db-bea4-7ed52734fdad} + + + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API\Vulkan + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\API + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\Program + + + Core\State + + + Core\State + + + Core + + + Core + + + Effects\Shadows + + + Effects\SkyBox + + + Scene\Camera + + + Scene\Camera + + + Scene\Lights + + + Scene\Lights + + + Scene\Material + + + Testing + + + Utils + + + Utils\Scripting + + + Utils\Video + + + Utils\Video + + + Utils\Timing + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\UI + + + Utils\Image + + + Utils\Image + + + Utils\Algorithm + + + Core\Platform + + + Core\Platform + + + Core\Platform\Windows + + + Core\Platform\Windows + + + Core\Platform\Linux + + + Core\Platform\Linux + + + Core\BufferTypes + + + Core\BufferTypes + + + Core\BufferTypes + + + Core\API\D3D12 + + + Core\API\D3D12 + + + Core\BufferTypes + + + Core\BufferTypes + + + Utils\Perception + + + Utils\Perception + + + + Utils\UI + + + Core\Platform + + + Utils\SampleGenerators + + + Utils\SampleGenerators + + + Core\API + + + Core\API + + + Core + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui + + + Externals\dear_imgui_addons + + + Utils\Scripting + + + Utils + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph\BasePasses + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + RenderGraph + + + Utils + + + Utils\Timing + + + Utils\Scripting + + + Utils\Timing + + + Core\API + + + Core\API\Vulkan + + + Core\API\D3D12 + + + Scene + + + Externals\mikktspace + + + Utils\SampleGenerators + + + Scene + + + Effects\AmbientOcclusion + + + Effects\FXAA + + + Effects\TAA + + + Effects\ToneMapping + + + Scene\ParticleSystem + + + Effects\Utils + + + Scene\Animation + + + Scene\Animation + + + Scene\Importers + + + Scene\Importers + + + RenderPasses + + + RenderPasses + + + RenderPasses + + + RenderPasses + + + RenderPasses + + + Raytracing\RtProgram + + + Raytracing\RtProgram + + + Raytracing\RtProgram + + + Raytracing + + + Raytracing + + + Raytracing + + + Raytracing + + + Raytracing + + + Utils\Debug + + + Utils\Algorithm + + + Utils\Algorithm + + + Utils\Algorithm + + + Utils\Sampling + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Effects + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\Framework\Shaders + + + Data\RenderPasses + + + Data + + + Data + + + Data + + + ShadingUtils + + + ShadingUtils + + + ShadingUtils + + + ShadingUtils + + + ShadingUtils + + + Data\Framework\Shaders + + + ShadingUtils + + + Data\Framework\Shaders + + + ShadingUtils + + + Data\RenderPasses + + + Data + + + + + Data\Effects + + + + + Data + + + + + + + + Utils\Algorithm + + + Utils\Debug + + + Utils\Math + + + Utils\Algorithm + + + Utils\Math + + + Utils\Color + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Utils\Math + + + Utils\Math + + + Utils\Math + + + Utils\Sampling\Pseudorandom + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Lights + + + Experimental\Scene\Material + + + Experimental\Scene\Material + + + Utils\Math + + + Utils\Math + + + Experimental\Scene\Lights + + + Utils\Math + + + Utils\Algorithm + + + Utils\Debug + + + Utils\Algorithm + + + Utils\Sampling + + + Utils\Sampling + + + Utils\Sampling + + + Utils\Sampling\Pseudorandom + + + Utils\Sampling + + + Utils\Sampling + + + Utils\Sampling\Pseudorandom + + + Utils\Math + + + \ No newline at end of file diff --git a/Source/Falcor/FalcorExperimental.h b/Source/Falcor/FalcorExperimental.h new file mode 100644 index 000000000..dd9ce4669 --- /dev/null +++ b/Source/Falcor/FalcorExperimental.h @@ -0,0 +1,31 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Experimental/Scene/Lights/EnvProbe.h" +#include "Experimental/Scene/Lights/EmissiveLightSampler.h" +#include "Experimental/Scene/Lights/EmissiveUniformSampler.h" diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/HitProgram.cpp b/Source/Falcor/Raytracing/RtProgram/HitProgram.cpp similarity index 97% rename from Framework/Source/Experimental/Raytracing/RtProgram/HitProgram.cpp rename to Source/Falcor/Raytracing/RtProgram/HitProgram.cpp index ce06b5606..87fb9a63c 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/HitProgram.cpp +++ b/Source/Falcor/Raytracing/RtProgram/HitProgram.cpp @@ -25,11 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "HitProgram.h" -#include "..\RtShader.h" -#include "RtProgramVersion.h" -#include "Graphics/Program/ShaderLibrary.h" namespace Falcor { diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/HitProgram.h b/Source/Falcor/Raytracing/RtProgram/HitProgram.h similarity index 98% rename from Framework/Source/Experimental/Raytracing/RtProgram/HitProgram.h rename to Source/Falcor/Raytracing/RtProgram/HitProgram.h index 1d8496fd2..3e12c8f56 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/HitProgram.h +++ b/Source/Falcor/Raytracing/RtProgram/HitProgram.h @@ -26,8 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Program/Program.h" #include "RtProgramVersion.h" +#include "Core/Program/Program.h" namespace Falcor { @@ -58,4 +58,4 @@ namespace Falcor static HitProgram::SharedPtr createCommon(const std::string& filename, const std::string& closestHitEntry, const std::string& anyHitEntry, const std::string& intersectionEntry, const DefineList& programDefines, bool fromFile, uint32_t maxPayloadSize, uint32_t maxAttributeSize, Shader::CompilerFlags flags); virtual ProgramVersion::SharedPtr createProgramVersion(std::string& log, const Shader::Blob shaderBlob[kShaderCount], const ProgramReflectors& reflectors) const override; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgram.cpp b/Source/Falcor/Raytracing/RtProgram/RtProgram.cpp similarity index 97% rename from Framework/Source/Experimental/Raytracing/RtProgram/RtProgram.cpp rename to Source/Falcor/Raytracing/RtProgram/RtProgram.cpp index baebc30b0..b15410397 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgram.cpp +++ b/Source/Falcor/Raytracing/RtProgram/RtProgram.cpp @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtProgram.h" -#include "API/LowLevel/RootSignature.h" -#include "Graphics/Program/ShaderLibrary.h" namespace Falcor { @@ -118,6 +116,12 @@ namespace Falcor return *this; } + RtProgram::Desc& RtProgram::Desc::addDefines(const DefineList& defines) + { + for (auto it : defines) addDefine(it.first, it.second); + return *this; + } + RtProgram::SharedPtr RtProgram::create(const Desc& desc, uint32_t maxPayloadSize, uint32_t maxAttributesSize) { if (desc.mRayGen.libraryIndex == -1) @@ -138,7 +142,8 @@ namespace Falcor if (mReflectionDirty) { // Create the global reflector and root-signature - mpGlobalReflector = ProgramReflection::create(nullptr, ProgramReflection::ResourceScope::Global, std::string()); + std::string log; + mpGlobalReflector = ProgramReflection::create(nullptr, ProgramReflection::ResourceScope::Global, log); mpGlobalReflector = ProgramReflection::merge(*mpGlobalReflector, *mpRayGenProgram->getGlobalReflector()); for (const auto m : mMissProgs) diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgram.h b/Source/Falcor/Raytracing/RtProgram/RtProgram.h similarity index 86% rename from Framework/Source/Experimental/Raytracing/RtProgram/RtProgram.h rename to Source/Falcor/Raytracing/RtProgram/RtProgram.h index a9509edb9..56acc9d4f 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgram.h +++ b/Source/Falcor/Raytracing/RtProgram/RtProgram.h @@ -26,33 +26,34 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once +#include "Core/Program/Program.h" #include "SingleShaderProgram.h" #include "HitProgram.h" +#include "Core/API/RootSignature.h" namespace Falcor { - class ShaderLibrary; - - class RtProgram : public ProgramBase, public std::enable_shared_from_this + class dlldecl RtProgram : public ProgramBase, public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; using DefineList = Program::DefineList; - class Desc + class dlldecl Desc { public: Desc() = default; Desc(const std::string& filename) { addShaderLibrary(filename); } - Desc(const std::shared_ptr& pLibrary) { addShaderLibrary(pLibrary); } + Desc(const ShaderLibrary::SharedPtr& pLibrary) { addShaderLibrary(pLibrary); } - Desc& addShaderLibrary(const std::shared_ptr& pLibrary); + Desc& addShaderLibrary(const ShaderLibrary::SharedPtr& pLibrary); Desc& addShaderLibrary(const std::string& filename); Desc& setRayGen(const std::string& raygen); Desc& addMiss(uint32_t missIndex, const std::string& miss); Desc& addHitGroup(uint32_t hitIndex, const std::string& closestHit, const std::string& anyHit, const std::string& intersection = ""); Desc& addDefine(const std::string& define, const std::string& value); + Desc& addDefines(const DefineList& defines); /** Get the compiler flags */ @@ -63,7 +64,7 @@ namespace Falcor Desc& setCompilerFlags(Shader::CompilerFlags flags) { shaderFlags = flags; return *this; } private: friend class RtProgram; - std::vector> mShaderLibraries; + std::vector mShaderLibraries; DefineList mDefineList; struct ShaderEntry @@ -108,8 +109,8 @@ namespace Falcor virtual bool setDefines(const DefineList& dl) override; virtual const DefineList& getDefines() const override { assert(false); static DefineList dummy; return dummy; /* not well defined if the ray programs have mismatching set of defines */ } - const std::shared_ptr& getGlobalRootSignature() const { updateReflection(); return mpGlobalRootSignature; } - const std::shared_ptr& getGlobalReflector() const { updateReflection(); return mpGlobalReflector; } + const RootSignature::SharedPtr& getGlobalRootSignature() const { updateReflection(); return mpGlobalRootSignature; } + const ProgramReflection::SharedPtr& getGlobalReflector() const { updateReflection(); return mpGlobalReflector; } private: @@ -124,7 +125,7 @@ namespace Falcor RayGenProgram::SharedPtr mpRayGenProgram; mutable bool mReflectionDirty = true; - mutable std::shared_ptr mpGlobalRootSignature; - mutable std::shared_ptr mpGlobalReflector; + mutable RootSignature::SharedPtr mpGlobalRootSignature; + mutable ProgramReflection::SharedPtr mpGlobalReflector; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgramVersion.cpp b/Source/Falcor/Raytracing/RtProgram/RtProgramVersion.cpp similarity index 94% rename from Framework/Source/Experimental/Raytracing/RtProgram/RtProgramVersion.cpp rename to Source/Falcor/Raytracing/RtProgram/RtProgramVersion.cpp index 9caa7bf03..7c6325de7 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgramVersion.cpp +++ b/Source/Falcor/Raytracing/RtProgram/RtProgramVersion.cpp @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtProgramVersion.h" -#include "API/Device.h" -#include "API/D3D12/D3D12State.h" #include "Utils/StringUtils.h" namespace Falcor @@ -36,7 +34,7 @@ namespace Falcor uint64_t RtProgramVersion::sProgId = 0; ProgramReflection::SharedPtr createProgramReflection(const Shader::SharedConstPtr pShaders[], std::string& log); - RtProgramVersion::RtProgramVersion(std::shared_ptr pReflector, Type progType, RtShader::SharedPtr const* ppShaders, size_t shaderCount, const std::string& name, uint32_t maxPayloadSize, uint32_t maxAttributeSize) + RtProgramVersion::RtProgramVersion(ProgramReflection::SharedPtr pReflector, Type progType, RtShader::SharedPtr const* ppShaders, size_t shaderCount, const std::string& name, uint32_t maxPayloadSize, uint32_t maxAttributeSize) : ProgramVersion(pReflector, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, name) , mType(progType) , mMaxAttributeSize(maxAttributeSize) @@ -66,11 +64,6 @@ namespace Falcor bool RtProgramVersion::initCommon(std::string& log) { - if (init(log) == false) - { - return false; - } - // Create the root signature mpLocalRootSignature = RootSignature::create(mpReflector.get(), true); return true; diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgramVersion.h b/Source/Falcor/Raytracing/RtProgram/RtProgramVersion.h similarity index 89% rename from Framework/Source/Experimental/Raytracing/RtProgram/RtProgramVersion.h rename to Source/Falcor/Raytracing/RtProgram/RtProgramVersion.h index 1939ddaf2..2838fe3ae 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/RtProgramVersion.h +++ b/Source/Falcor/Raytracing/RtProgram/RtProgramVersion.h @@ -26,14 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Program/ProgramVersion.h" +#include "Core/Program/ProgramVersion.h" #include "../RtShader.h" -#include "API/VAO.h" +#include "Core/API/RootSignature.h" namespace Falcor { - class RootSignature; - // The inheritence here is just so that we could work with Program. We're not actually using anything from ProgramVersion class RtProgramVersion : public ProgramVersion, inherit_shared_from_this { @@ -56,7 +54,7 @@ namespace Falcor RtShader::SharedConstPtr getShader(ShaderType type) const; - std::shared_ptr getLocalRootSignature() const { return mpLocalRootSignature; } + RootSignature::SharedPtr getLocalRootSignature() const { return mpLocalRootSignature; } const std::wstring& getExportName() const { return mExportName; } @@ -69,8 +67,8 @@ namespace Falcor static SharedPtr createSingleShaderProgram(RtShader::SharedPtr pShader, std::string& log, const std::string& name, ProgramReflection::SharedPtr pLocalReflector, uint32_t maxPayloadSize, uint32_t maxAttributeSize); bool initCommon(std::string& log); - RtProgramVersion(std::shared_ptr pReflector, Type progType, RtShader::SharedPtr const* ppShaders, size_t shaderCount, const std::string& name, uint32_t maxPayloadSize, uint32_t maxAttributeSize); - std::shared_ptr mpLocalRootSignature; + RtProgramVersion(ProgramReflection::SharedPtr pReflector, Type progType, const RtShader::SharedPtr ppShaders[], size_t shaderCount, const std::string& name, uint32_t maxPayloadSize, uint32_t maxAttributeSize); + RootSignature::SharedPtr mpLocalRootSignature; Type mType; std::wstring mExportName; diff --git a/Framework/Source/Experimental/Raytracing/RtProgram/SingleShaderProgram.h b/Source/Falcor/Raytracing/RtProgram/SingleShaderProgram.h similarity index 97% rename from Framework/Source/Experimental/Raytracing/RtProgram/SingleShaderProgram.h rename to Source/Falcor/Raytracing/RtProgram/SingleShaderProgram.h index 86bd5a3db..0767bb8e0 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgram/SingleShaderProgram.h +++ b/Source/Falcor/Raytracing/RtProgram/SingleShaderProgram.h @@ -26,10 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Program/Program.h" -#include "..\RtShader.h" +#include "Core/Program/Program.h" +#include "../RtShader.h" #include "RtProgramVersion.h" -#include "Graphics/Program/ShaderLibrary.h" +#include "Core/Program/ShaderLibrary.h" namespace Falcor { diff --git a/Framework/Source/Experimental/Raytracing/RtProgramVars.cpp b/Source/Falcor/Raytracing/RtProgramVars.cpp similarity index 74% rename from Framework/Source/Experimental/Raytracing/RtProgramVars.cpp rename to Source/Falcor/Raytracing/RtProgramVars.cpp index f2ff419ad..74f2f31da 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgramVars.cpp +++ b/Source/Falcor/Raytracing/RtProgramVars.cpp @@ -25,14 +25,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtProgramVars.h" -#include "API/Device.h" +#include "Core/API/Device.h" #include "RtStateObject.h" namespace Falcor { - static bool checkParams(RtProgram::SharedPtr pProgram, RtScene::SharedPtr pScene) + static bool checkParams(RtProgram::SharedPtr pProgram, Scene::SharedPtr pScene) { if (pScene == nullptr) { @@ -48,14 +48,14 @@ namespace Falcor return true; } - RtProgramVars::RtProgramVars(RtProgram::SharedPtr pProgram, RtScene::SharedPtr pScene) : mpProgram(pProgram), mpScene(pScene) + RtProgramVars::RtProgramVars(const RtProgram::SharedPtr& pProgram, const Scene::SharedPtr& pScene, bool perMeshHitEntry) : mpProgram(pProgram), mpScene(pScene), mPerMeshHitEntry(perMeshHitEntry) { mpRtVarsHelper = RtVarsContext::create(); } - RtProgramVars::SharedPtr RtProgramVars::create(RtProgram::SharedPtr pProgram, RtScene::SharedPtr pScene) + RtProgramVars::SharedPtr RtProgramVars::create(const RtProgram::SharedPtr& pProgram, const Scene::SharedPtr& pScene, bool perMeshHitEntry) { - SharedPtr pVars = SharedPtr(new RtProgramVars(pProgram, pScene)); + SharedPtr pVars = SharedPtr(new RtProgramVars(pProgram, pScene, perMeshHitEntry)); if ((checkParams(pProgram, pScene) == false) || (pVars->init() == false)) { return nullptr; @@ -67,8 +67,9 @@ namespace Falcor void getSigSizeAndCreateVars(ProgType pProg, uint32_t& maxRootSigSize, GraphicsVars::SharedPtr pVars[], uint32_t varCount) { RtProgramVersion::SharedConstPtr pVersion = pProg->getActiveVersion(); + assert(pVersion); maxRootSigSize = max(pVersion->getLocalRootSignature()->getSizeInBytes(), maxRootSigSize); - for(uint32_t i = 0 ; i < varCount ; i++) + for (uint32_t i = 0 ; i < varCount ; i++) { pVars[i] = GraphicsVars::create(pProg->getLocalReflector(), true, pVersion->getLocalRootSignature()); } @@ -85,7 +86,7 @@ namespace Falcor mFirstHitVarEntry = kFirstMissRecordIndex + mMissProgCount; mMissVars.resize(mMissProgCount); mHitVars.resize(mHitProgCount); - uint32_t recordCountPerHit = mpScene->getGeometryCount(mHitProgCount); + uint32_t recordCountPerHit = mPerMeshHitEntry ? mpScene.lock()->getMeshCount() : 1; for (uint32_t i = 0 ; i < mHitProgCount; i++) { @@ -138,7 +139,7 @@ namespace Falcor // +------------+---------+---------+-----+--------+---------+--------+-----+--------+--------+-----+--------+-----+--------+--------+-----+--------+ // // The first record is the ray gen, followed by the miss records, followed by the meshes records. - // For each mesh we have N hit records, N == number of mesh instances in the model + // For each mesh we have N hit records, N == number of ray types in the program // The size of each record is mRecordSize // // If this layout changes, we also need to change the constants kRayGenRecordIndex and kFirstMissRecordIndex @@ -165,6 +166,7 @@ namespace Falcor bool applyRtProgramVars(uint8_t* pRecord, const RtProgramVersion* pProgVersion, const RtStateObject* pRtso, ProgramVars* pVars, RtVarsContext* pContext) { + assert(pProgVersion); MAKE_SMART_COM_PTR(ID3D12StateObjectProperties); ID3D12StateObjectPropertiesPtr pRtsoPtr = pRtso->getApiHandle(); memcpy(pRecord, pRtsoPtr->GetShaderIdentifier(pProgVersion->getExportName().c_str()), D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES); @@ -173,50 +175,76 @@ namespace Falcor return pVars->applyProgramVarsCommon(pContext, true); } - bool RtProgramVars::apply(RenderContext* pCtx, RtStateObject* pRtso) + bool RtProgramVars::updateSBT(RenderContext* pCtx, RtStateObject* pRtso) { - // We always have a ray-gen program, apply it first - uint8_t* pRayGenRecord = getRayGenRecordPtr(); - if (!applyRtProgramVars(pRayGenRecord, mpProgram->getRayGenProgram()->getActiveVersion().get(), pRtso, getRayGenVars().get(), mpRtVarsHelper.get())) + if (mpLastUsedRtpso == pRtso) return true; + mpLastUsedRtpso = pRtso; + { - return false; + PROFILE("applyRayGen"); + // We always have a ray-gen program, apply it first + uint8_t* pRayGenRecord = getRayGenRecordPtr(); + if (!applyRtProgramVars(pRayGenRecord, mpProgram->getRayGenProgram()->getActiveVersion().get(), pRtso, getRayGenVars().get(), mpRtVarsHelper.get())) + { + return false; + } } - // Loop over the rays - uint32_t hitCount = mpProgram->getHitProgramCount(); - for (uint32_t h = 0; h < hitCount; h++) { - if(mpProgram->getHitProgram(h)) + PROFILE("applyHit"); + // Loop over the rays + uint32_t hitCount = mpProgram->getHitProgramCount(); + for (uint32_t h = 0; h < hitCount; h++) { - for (uint32_t i = 0; i < mpScene->getGeometryCount(hitCount); i++) + if (mpProgram->getHitProgram(h)) { - uint8_t* pHitRecord = getHitRecordPtr(h, i); - if (!applyRtProgramVars(pHitRecord, mpProgram->getHitProgram(h)->getActiveVersion().get(), pRtso, getHitVars(h)[i].get(), mpRtVarsHelper.get())) + uint32_t entriesPerRayType = mPerMeshHitEntry ? mpScene.lock()->getMeshCount() : 1; + for (uint32_t i = 0; i < entriesPerRayType; i++) { - return false; + uint8_t* pHitRecord = getHitRecordPtr(h, i); + if (!applyRtProgramVars(pHitRecord, mpProgram->getHitProgram(h)->getActiveVersion().get(), pRtso, getHitVars(h)[i].get(), mpRtVarsHelper.get())) + { + return false; + } } } } } - for (uint32_t m = 0; m < mpProgram->getMissProgramCount(); m++) { - if(mpProgram->getMissProgram(m)) + PROFILE("applyMiss"); + for (uint32_t m = 0; m < mpProgram->getMissProgramCount(); m++) { - uint8_t* pMissRecord = getMissRecordPtr(m); - if (!applyRtProgramVars(pMissRecord, mpProgram->getMissProgram(m)->getActiveVersion().get(), pRtso, getMissVars(m).get(), mpRtVarsHelper.get())) + if (mpProgram->getMissProgram(m)) { - return false; + uint8_t* pMissRecord = getMissRecordPtr(m); + if (!applyRtProgramVars(pMissRecord, mpProgram->getMissProgram(m)->getActiveVersion().get(), pRtso, getMissVars(m).get(), mpRtVarsHelper.get())) + { + return false; + } } } } - if (!mpGlobalVars->applyProgramVarsCommon(pCtx, true)) { - return false; + PROFILE("updateSBT"); + pCtx->updateBuffer(mpShaderTable.get(), mShaderTableData.data()); } - pCtx->updateBuffer(mpShaderTable.get(), mShaderTableData.data()); + return true; + } + + bool RtProgramVars::apply(RenderContext* pCtx, RtStateObject* pRtso) + { + if (!updateSBT(pCtx, pRtso)) return false; + + { + PROFILE("applyGlobal"); + if (!mpGlobalVars->applyProgramVarsCommon(pCtx, true)) + { + return false; + } + } return true; } } diff --git a/Framework/Source/Experimental/Raytracing/RtProgramVars.h b/Source/Falcor/Raytracing/RtProgramVars.h similarity index 85% rename from Framework/Source/Experimental/Raytracing/RtProgramVars.h rename to Source/Falcor/Raytracing/RtProgramVars.h index 8e3809f1c..029900f78 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgramVars.h +++ b/Source/Falcor/Raytracing/RtProgramVars.h @@ -26,18 +26,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once +#include "Core/Program/ProgramVars.h" #include "RtProgram/RtProgram.h" -#include "RtScene.h" -#include "API/Buffer.h" -#include "Graphics/Program/ProgramVars.h" #include "RtProgramVarsHelper.h" +#include "Scene/Scene.h" namespace Falcor { - class RenderContext; class RtStateObject; - class RtProgramVars : public std::enable_shared_from_this + class dlldecl RtProgramVars : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -45,7 +43,7 @@ namespace Falcor using VarsVector = std::vector; - static SharedPtr create(RtProgram::SharedPtr pProgram, RtScene::SharedPtr pScene); + static SharedPtr create(const RtProgram::SharedPtr& pProgram, const Scene::SharedPtr& pScene, bool perMeshHitEntry = true); VarsVector& getHitVars(uint32_t rayID) { return mHitVars[rayID]; } const GraphicsVars::SharedPtr& getRayGenVars() { return mRayGenVars; } @@ -62,7 +60,8 @@ namespace Falcor uint32_t getHitProgramsCount() const { return mHitProgCount; } uint32_t getMissProgramsCount() const { return mMissProgCount; } uint32_t getHitRecordsCount() const { return mHitRecordCount; } - + + bool hasPerMeshHitEntry() const { return mPerMeshHitEntry; } private: static const uint32_t kRayGenRecordIndex = 0; static const uint32_t kFirstMissRecordIndex = 1; @@ -71,9 +70,9 @@ namespace Falcor uint32_t mHitRecordCount = 0; ///< Total number of hit records in shader table uint32_t mFirstHitVarEntry = 0; - RtProgramVars(RtProgram::SharedPtr pProgram, RtScene::SharedPtr pScene); + RtProgramVars(const RtProgram::SharedPtr& pProgram, const Scene::SharedPtr& pScene, bool perMeshHitEntry); RtProgram::SharedPtr mpProgram; - RtScene::SharedPtr mpScene; + std::weak_ptr mpScene; uint32_t mRecordSize; Buffer::SharedPtr mpShaderTable; @@ -83,11 +82,14 @@ namespace Falcor bool init(); + bool updateSBT(RenderContext* pCtx, RtStateObject* pRtso); + const RtStateObject* mpLastUsedRtpso = nullptr; GraphicsVars::SharedPtr mpGlobalVars; GraphicsVars::SharedPtr mRayGenVars; std::vector mHitVars; std::vector mShaderTableData; VarsVector mMissVars; RtVarsContext::SharedPtr mpRtVarsHelper; + bool mPerMeshHitEntry; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/Raytracing/RtProgramVarsHelper.cpp b/Source/Falcor/Raytracing/RtProgramVarsHelper.cpp similarity index 90% rename from Framework/Source/Experimental/Raytracing/RtProgramVarsHelper.cpp rename to Source/Falcor/Raytracing/RtProgramVarsHelper.cpp index 784c5d3ea..f4e0bb03b 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgramVarsHelper.cpp +++ b/Source/Falcor/Raytracing/RtProgramVarsHelper.cpp @@ -25,12 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtProgramVarsHelper.h" -#include "API/Device.h" -#include "API/LowLevel/DescriptorPool.h" -#include "API/D3D12/LowLevel/D3D12DescriptorData.h" -#include "API/D3D12/LowLevel/D3D12DescriptorHeap.h" +#include "Core/API/Device.h" namespace Falcor { @@ -54,17 +51,22 @@ namespace Falcor mpList = nullptr; } - void RtVarsContext::resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo) + bool RtVarsContext::resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo) { - gpDevice->getRenderContext()->resourceBarrier(pResource, newState, pViewInfo); + return gpDevice->getRenderContext()->resourceBarrier(pResource, newState, pViewInfo); + } + + void RtVarsContext::uavBarrier(const Resource* pResource) + { + gpDevice->getRenderContext()->uavBarrier(pResource); } HRESULT RtVarsCmdList::QueryInterface(REFIID riid, void **ppvObject) { if (riid == __uuidof(ID3D12CommandList)) { - *ppvObject = dynamic_cast(this); - return S_OK; + *ppvObject = dynamic_cast(this); + return S_OK; } else if (riid == __uuidof(ID3D12GraphicsCommandList4)) { @@ -88,8 +90,8 @@ namespace Falcor } else if (riid == __uuidof(ID3D12GraphicsCommandList)) { - *ppvObject = dynamic_cast(this); - return S_OK; + *ppvObject = dynamic_cast(this); + return S_OK; } else { diff --git a/Framework/Source/Experimental/Raytracing/RtProgramVarsHelper.h b/Source/Falcor/Raytracing/RtProgramVarsHelper.h similarity index 96% rename from Framework/Source/Experimental/Raytracing/RtProgramVarsHelper.h rename to Source/Falcor/Raytracing/RtProgramVarsHelper.h index ae356fbe3..f07d7f40f 100644 --- a/Framework/Source/Experimental/Raytracing/RtProgramVarsHelper.h +++ b/Source/Falcor/Raytracing/RtProgramVarsHelper.h @@ -26,12 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "API/CopyContext.h" -#include "API/LowLevel/RootSignature.h" +#include "Core/API/CopyContext.h" +#include "Core/API/RootSignature.h" namespace Falcor { - class RtVarsCmdList : public ID3D12GraphicsCommandList4 + class dlldecl RtVarsCmdList : public ID3D12GraphicsCommandList4 { public: using SharedPtr = std::shared_ptr; @@ -72,7 +72,7 @@ namespace Falcor void IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY PrimitiveTopology) { should_not_get_here(); } void RSSetViewports(UINT NumViewports, const D3D12_VIEWPORT *pViewports) { should_not_get_here(); } void RSSetScissorRects(UINT NumRects, const D3D12_RECT *pRects) { should_not_get_here(); } - void OMSetBlendFactor(const FLOAT BlendFactor[ 4 ]) { should_not_get_here(); } + void OMSetBlendFactor(const FLOAT BlendFactor[4]) { should_not_get_here(); } void OMSetRenderTargets(UINT NumRenderTargetDescriptors, const D3D12_CPU_DESCRIPTOR_HANDLE *pRenderTargetDescriptors, BOOL RTsSingleHandleToDescriptorRange, const D3D12_CPU_DESCRIPTOR_HANDLE *pDepthStencilDescriptor) { should_not_get_here(); } void OMSetStencilRef(UINT StencilRef) { should_not_get_here(); } void SetPipelineState(ID3D12PipelineState *pPipelineState) { should_not_get_here(); } @@ -90,7 +90,7 @@ namespace Falcor void IASetVertexBuffers(UINT StartSlot, UINT NumViews, const D3D12_VERTEX_BUFFER_VIEW *pViews) { should_not_get_here(); } void SOSetTargets(UINT StartSlot, UINT NumViews, const D3D12_STREAM_OUTPUT_BUFFER_VIEW *pViews) { should_not_get_here(); } void ClearDepthStencilView(D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView, D3D12_CLEAR_FLAGS ClearFlags, FLOAT Depth, UINT8 Stencil, UINT NumRects, const D3D12_RECT *pRects) { should_not_get_here(); } - void ClearRenderTargetView(D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetView, const FLOAT ColorRGBA[ 4 ], UINT NumRects, const D3D12_RECT *pRects) { should_not_get_here(); } + void ClearRenderTargetView(D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetView, const FLOAT ColorRGBA[4], UINT NumRects, const D3D12_RECT *pRects) { should_not_get_here(); } void ClearUnorderedAccessViewFloat(D3D12_GPU_DESCRIPTOR_HANDLE ViewGPUHandleInCurrentHeap, D3D12_CPU_DESCRIPTOR_HANDLE ViewCPUHandle, ID3D12Resource *pResource, const FLOAT Values[4], UINT NumRects, const D3D12_RECT *pRects) { should_not_get_here(); } void ClearUnorderedAccessViewUint(D3D12_GPU_DESCRIPTOR_HANDLE ViewGPUHandleInCurrentHeap, D3D12_CPU_DESCRIPTOR_HANDLE ViewCPUHandle, ID3D12Resource *pResource, const UINT Values[4], UINT NumRects, const D3D12_RECT *pRects) { should_not_get_here(); } void DiscardResource(ID3D12Resource *pResource, const D3D12_DISCARD_REGION *pRegion) { should_not_get_here(); } @@ -134,7 +134,7 @@ namespace Falcor RootSignature::SharedPtr mpRootSignature; }; - class RtVarsContext : public CopyContext, inherit_shared_from_this + class dlldecl RtVarsContext : public CopyContext { public: using SharedPtr = std::shared_ptr; @@ -143,8 +143,10 @@ namespace Falcor static SharedPtr create(); const LowLevelContextData::SharedPtr& getLowLevelData() const override { return mpLowLevelData; } - void resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo = nullptr) override; + bool resourceBarrier(const Resource* pResource, Resource::State newState, const ResourceViewInfo* pViewInfo = nullptr) override; RtVarsCmdList::SharedPtr getRtVarsCmdList() const { return mpList; } + + void uavBarrier(const Resource* pResource) override; private: RtVarsContext(); RtVarsCmdList::SharedPtr mpList; diff --git a/Framework/Source/Experimental/Raytracing/RtShader.cpp b/Source/Falcor/Raytracing/RtShader.cpp similarity index 98% rename from Framework/Source/Experimental/Raytracing/RtShader.cpp rename to Source/Falcor/Raytracing/RtShader.cpp index 345077e94..0c39c0a79 100644 --- a/Framework/Source/Experimental/Raytracing/RtShader.cpp +++ b/Source/Falcor/Raytracing/RtShader.cpp @@ -25,9 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtShader.h" -#include "Utils/StringUtils.h" namespace Falcor { diff --git a/Framework/Source/Experimental/Raytracing/RtShader.h b/Source/Falcor/Raytracing/RtShader.h similarity index 92% rename from Framework/Source/Experimental/Raytracing/RtShader.h rename to Source/Falcor/Raytracing/RtShader.h index 8bda3e418..61024bf95 100644 --- a/Framework/Source/Experimental/Raytracing/RtShader.h +++ b/Source/Falcor/Raytracing/RtShader.h @@ -26,16 +26,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Program/Program.h" -#include "API/Shader.h" +#include "Core/API/Shader.h" namespace Falcor { - class RtShader : public Shader, inherit_shared_from_this + class dlldecl RtShader : public Shader, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; static SharedPtr create(const Blob& shaderBlob, const std::string& entryPointName, ShaderType Type, Shader::CompilerFlags flags, std::string& log); ~RtShader(); diff --git a/Framework/Source/Experimental/Raytracing/RtState.cpp b/Source/Falcor/Raytracing/RtState.cpp similarity index 97% rename from Framework/Source/Experimental/Raytracing/RtState.cpp rename to Source/Falcor/Raytracing/RtState.cpp index a0ac5bb2e..9270bd72b 100644 --- a/Framework/Source/Experimental/Raytracing/RtState.cpp +++ b/Source/Falcor/Raytracing/RtState.cpp @@ -25,8 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtState.h" +#include "RtProgram/RtProgram.h" +#include "RtProgram/HitProgram.h" namespace Falcor { diff --git a/Framework/Source/Experimental/Raytracing/RtState.h b/Source/Falcor/Raytracing/RtState.h similarity index 93% rename from Framework/Source/Experimental/Raytracing/RtState.h rename to Source/Falcor/Raytracing/RtState.h index 9a993c181..548db5de7 100644 --- a/Framework/Source/Experimental/Raytracing/RtState.h +++ b/Source/Falcor/Raytracing/RtState.h @@ -26,13 +26,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "RtProgram/RtProgram.h" +#include "Core/State/StateGraph.h" #include "RtStateObject.h" -#include "Utils/Graph.h" +#include "RtProgram/RtProgram.h" namespace Falcor { - class RtState : public std::enable_shared_from_this + class dlldecl RtState : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -52,7 +52,7 @@ namespace Falcor RtState(); RtProgram::SharedPtr mpProgram; uint32_t mMaxTraceRecursionDepth = 1; - using StateGraph = Graph; + using StateGraph = StateGraph; StateGraph::SharedPtr mpRtsoGraph; RtStateObject::ProgramList createProgramList() const; diff --git a/Framework/Source/Experimental/Raytracing/RtStateObject.cpp b/Source/Falcor/Raytracing/RtStateObject.cpp similarity index 98% rename from Framework/Source/Experimental/Raytracing/RtStateObject.cpp rename to Source/Falcor/Raytracing/RtStateObject.cpp index bdf5b9be6..6e777f078 100644 --- a/Framework/Source/Experimental/Raytracing/RtStateObject.cpp +++ b/Source/Falcor/Raytracing/RtStateObject.cpp @@ -25,12 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RtStateObject.h" #include "RtStateObjectHelper.h" -#include "API/Device.h" #include "Utils/StringUtils.h" -#include "API/LowLevel/RootSignature.h" +#include "Core/API/Device.h" namespace Falcor { diff --git a/Framework/Source/Experimental/Raytracing/RtStateObject.h b/Source/Falcor/Raytracing/RtStateObject.h similarity index 87% rename from Framework/Source/Experimental/Raytracing/RtStateObject.h rename to Source/Falcor/Raytracing/RtStateObject.h index 018f09d51..6a124c4b8 100644 --- a/Framework/Source/Experimental/Raytracing/RtStateObject.h +++ b/Source/Falcor/Raytracing/RtStateObject.h @@ -30,7 +30,7 @@ namespace Falcor { - class RtStateObject : public std::enable_shared_from_this + class dlldecl RtStateObject : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; @@ -39,17 +39,17 @@ namespace Falcor using ProgramList = std::vector; - class Desc + class dlldecl Desc { public: Desc& setProgramList(const ProgramList& list) { mProgList = list; return *this; } Desc& setMaxTraceRecursionDepth(uint32_t maxDepth) { mMaxTraceRecursionDepth = maxDepth; return *this; } - Desc& setGlobalRootSignature(const std::shared_ptr& pRootSig) { mpGlobalRootSignature = pRootSig; return *this; } + Desc& setGlobalRootSignature(const RootSignature::SharedPtr& pRootSig) { mpGlobalRootSignature = pRootSig; return *this; } bool operator==(const Desc& other) const; private: ProgramList mProgList; - std::shared_ptr mpGlobalRootSignature; + RootSignature::SharedPtr mpGlobalRootSignature; uint32_t mMaxTraceRecursionDepth = 1; friend RtStateObject; }; @@ -59,7 +59,7 @@ namespace Falcor const ProgramList& getProgramList() const { return mDesc.mProgList; } uint32_t getMaxTraceRecursionDepth() const { return mDesc.mMaxTraceRecursionDepth; } - const std::shared_ptr& getGlobalRootSignature() const { return mDesc.mpGlobalRootSignature; } + const RootSignature::SharedPtr& getGlobalRootSignature() const { return mDesc.mpGlobalRootSignature; } const Desc& getDesc() const { return mDesc; } private: RtStateObject(const Desc& d) : mDesc(d) {} diff --git a/Framework/Source/Experimental/Raytracing/RtStateObjectHelper.h b/Source/Falcor/Raytracing/RtStateObjectHelper.h similarity index 99% rename from Framework/Source/Experimental/Raytracing/RtStateObjectHelper.h rename to Source/Falcor/Raytracing/RtStateObjectHelper.h index 0081a706b..6918a8a81 100644 --- a/Framework/Source/Experimental/Raytracing/RtStateObjectHelper.h +++ b/Source/Falcor/Raytracing/RtStateObjectHelper.h @@ -30,7 +30,7 @@ namespace Falcor { - class RtStateObjectHelper + class dlldecl RtStateObjectHelper { public: ~RtStateObjectHelper() diff --git a/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp b/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp new file mode 100644 index 000000000..35525813f --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "BaseGraphicsPass.h" + +namespace Falcor +{ + BaseGraphicsPass::BaseGraphicsPass(const Program::Desc& progDesc, const Program::DefineList& programDefines) + { + auto pProg = GraphicsProgram::create(progDesc, programDefines); + if (!pProg) throw std::exception("Error when creating `BaseGraphicsPass`. Can't create the `GraphicsProgram`"); + + mpState = GraphicsState::create(); + if (!mpState) throw std::exception("Error when creating `BaseGraphicsPass`. Can't create the `GraphicsState`"); + mpState->setProgram(pProg); + + mpVars = GraphicsVars::create(pProg.get()); + if (!mpVars) throw std::exception("Error when creating `BaseGraphicsPass`. Can't create the `GraphicsVars`"); + } + + void BaseGraphicsPass::addDefine(const std::string& name, const std::string& value, bool updateVars) + { + mpState->getProgram()->addDefine(name, value); + if (updateVars) mpVars = GraphicsVars::create(mpState->getProgram().get()); + } + + void BaseGraphicsPass::removeDefine(const std::string& name, bool updateVars) + { + mpState->getProgram()->removeDefine(name); + if (updateVars) mpVars = GraphicsVars::create(mpState->getProgram().get()); + } + + void BaseGraphicsPass::setVars(const GraphicsVars::SharedPtr& pVars) + { + mpVars = pVars ? pVars : GraphicsVars::create(mpState->getProgram().get()); + } +} diff --git a/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h b/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h new file mode 100644 index 000000000..6df7dcc7b --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/BaseGraphicsPass.h @@ -0,0 +1,74 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/ProgramVars.h" +#include "Core/State/GraphicsState.h" + +namespace Falcor +{ + class dlldecl BaseGraphicsPass + { + public: + virtual ~BaseGraphicsPass() = default; + + /** Add a define + */ + void addDefine(const std::string& name, const std::string& value = "", bool updateVars = false); + + /** Remove a define + */ + void removeDefine(const std::string& name, bool updateVars = false); + + /** Get the program + */ + GraphicsProgram::SharedPtr getProgram() const { return mpState->getProgram(); } + + /** Get the state + */ + const GraphicsState::SharedPtr& getState() const { return mpState; } + + /** Get the vars + */ + const GraphicsVars::SharedPtr& getVars() const { return mpVars; } + + /** Set a vars object. Allows the user to override the internal vars, for example when one wants to share a vars object between different passes. + \param[in] pVars The new GraphicsVars object. If this is nullptr, then the pass will automatically create a new GraphicsVars object + */ + void setVars(const GraphicsVars::SharedPtr& pVars); + protected: + /** Create a new object. + \param[in] progDesc The program's desc + \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. + \return A new object, or nullptr if creation failed. + */ + BaseGraphicsPass(const Program::Desc& progDesc, const Program::DefineList& programDefines = Program::DefineList()); + GraphicsVars::SharedPtr mpVars; + GraphicsState::SharedPtr mpState; + }; +} diff --git a/Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp b/Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp new file mode 100644 index 000000000..92eef1c47 --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/ComputePass.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ComputePass.h" + +namespace Falcor +{ + ComputePass::ComputePass(const Program::Desc& desc, const Program::DefineList& defines, bool createVars) + { + auto pProg = ComputeProgram::create(desc, defines); + mpState = ComputeState::create(); + mpState->setProgram(pProg); + if (createVars) mpVars = ComputeVars::create(pProg.get()); + if ((createVars && !mpVars) || !mpState || !pProg) throw std::exception(); + } + + ComputePass::SharedPtr ComputePass::create(const std::string& csFile, const std::string& csEntry, const Program::DefineList& defines, bool createVars) + { + return create(Program::Desc().addShaderLibrary(csFile).csEntry(csEntry), defines, createVars); + } + + ComputePass::SharedPtr ComputePass::create(const Program::Desc& desc, const Program::DefineList& defines, bool createVars) + { + try + { + return SharedPtr(new ComputePass(desc, defines, createVars)); + } + catch (std::exception) { return nullptr; } + } + + void ComputePass::execute(ComputeContext* pContext, uint32_t nThreadX, uint32_t nThreadY, uint32_t nThreadZ) + { + assert(mpVars); + uvec3 threadGroupSize = mpState->getProgram()->getReflector()->getThreadGroupSize(); + uvec3 groups = div_round_up(glm::uvec3(nThreadX, nThreadY, nThreadZ), threadGroupSize); + pContext->dispatch(mpState.get(), mpVars.get(), groups); + } + + void ComputePass::addDefine(const std::string& name, const std::string& value, bool updateVars) + { + mpState->getProgram()->addDefine(name, value); + if (updateVars) mpVars = ComputeVars::create(mpState->getProgram().get()); + } + + void ComputePass::removeDefine(const std::string& name, bool updateVars) + { + mpState->getProgram()->removeDefine(name); + if (updateVars) mpVars = ComputeVars::create(mpState->getProgram().get()); + } + + void ComputePass::setVars(const ComputeVars::SharedPtr& pVars) + { + mpVars = pVars ? pVars : ComputeVars::create(mpState->getProgram().get()); + } +} diff --git a/Source/Falcor/RenderGraph/BasePasses/ComputePass.h b/Source/Falcor/RenderGraph/BasePasses/ComputePass.h new file mode 100644 index 000000000..8d47cf81a --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/ComputePass.h @@ -0,0 +1,97 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "..\RenderPass.h" +#include "Core\Program\ProgramVarsHelpers.h" + +namespace Falcor +{ + class dlldecl ComputePass : public std::enable_shared_from_this + { + public: + using SharedPtr = VarsSharedPtr; + + /** Create a new pass from a CS file + \param[in] csFile Filename + \param[in] csEntry Optional. Compute shader entry point name + \param[in] defines Program defines + \param[in] createVars Create program vars automatically, otherwise use setVars(). + */ + static SharedPtr create(const std::string& csFile, const std::string& csEntry = "main", const Program::DefineList& defines = Program::DefineList(), bool createVars = true); + + /** Create a new object + \param[in] desc The program's description + \param[in] defines Optional. A list of macro definitions to be patched into the shaders. + \param[in] createVars Create program vars automatically, otherwise use setVars(). + */ + static SharedPtr create(const Program::Desc& desc, const Program::DefineList& defines = Program::DefineList(), bool createVars = true); + + /** Execute the pass using the given compute-context + \param[in] pContext The compute context + \param[in] nThreadX The number of threads to dispatch in the X dimension (note that this is not the number of thread groups) + \param[in] nThreadY The number of threads to dispatch in the Y dimension (note that this is not the number of thread groups) + \param[in] nThreadZ The number of threads to dispatch in the Z dimension (note that this is not the number of thread groups) + */ + virtual void execute(ComputeContext* pContext, uint32_t nThreadX, uint32_t nThreadY, uint32_t nThreadZ = 1); + + /** Execute the pass using the given compute-context + \param[in] pContext The compute context + \param[in] nThreads The number of threads to dispatch in the XYZ dimensions (note that this is not the number of thread groups) + */ + virtual void execute(ComputeContext* pContext, const glm::uvec3& nThreads) { execute(pContext, nThreads.x, nThreads.y, nThreads.z); } + + /** Get the vars + */ + const ComputeVars::SharedPtr& getVars() const { assert(mpVars); return mpVars; }; + + /** Add a define + */ + void addDefine(const std::string& name, const std::string& value = "", bool updateVars = false); + + /** Remove a define + */ + void removeDefine(const std::string& name, bool updateVars = false); + + /** Get the program + */ + ComputeProgram::SharedPtr getProgram() const { return mpState->getProgram(); } + + /** Set a vars object. Allows the user to override the internal vars, for example when one wants to share a vars object between different passes. + \param[in] pVars The new GraphicsVars object. If this is nullptr, then the pass will automatically create a new GraphicsVars object + */ + void setVars(const ComputeVars::SharedPtr& pVars); + + /** Get the thread group size from the program + */ + uvec3 getThreadGroupSize() const { return mpState->getProgram()->getReflector()->getThreadGroupSize(); } + protected: + ComputePass(const Program::Desc& desc, const Program::DefineList& defines, bool createVars); + ComputeVars::SharedPtr mpVars; + ComputeState::SharedPtr mpState; + }; +} diff --git a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp b/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp new file mode 100644 index 000000000..5bcc2af47 --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "FullScreenPass.h" + +namespace Falcor +{ + namespace + { + struct FullScreenPassData + { + Buffer::SharedPtr pVertexBuffer; + Vao::SharedPtr pVao; + uint64_t objectCount = 0; + }; + + FullScreenPassData gFullScreenData; + + bool checkForViewportArray2Support() + { +#ifdef FALCOR_D3D12 + return false; +#elif defined FALCOR_VK + return false; +#else +#error Unknown API +#endif + } + struct Vertex + { + glm::vec2 screenPos; + glm::vec2 texCoord; + }; + +#ifdef FALCOR_VK +#define ADJUST_Y(a) (-(a)) +#else +#define ADJUST_Y(a) a +#endif + + const Vertex kVertices[] = + { + {glm::vec2(-1, ADJUST_Y(1)), glm::vec2(0, 0)}, + {glm::vec2(-1, ADJUST_Y(-1)), glm::vec2(0, 1)}, + {glm::vec2(1, ADJUST_Y(1)), glm::vec2(1, 0)}, + {glm::vec2(1, ADJUST_Y(-1)), glm::vec2(1, 1)}, + }; +#undef ADJUST_Y + + void initFullScreenData(Buffer::SharedPtr& pVB, Vao::SharedPtr& pVao) + { + // First time we got here. create VB and VAO + const uint32_t vbSize = (uint32_t)(sizeof(Vertex)*arraysize(kVertices)); + pVB = Buffer::create(vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, (void*)kVertices); + + // create VAO + VertexLayout::SharedPtr pLayout = VertexLayout::create(); + VertexBufferLayout::SharedPtr pBufLayout = VertexBufferLayout::create(); + pBufLayout->addElement("POSITION", 0, ResourceFormat::RG32Float, 1, 0); + pBufLayout->addElement("TEXCOORD", 8, ResourceFormat::RG32Float, 1, 1); + pLayout->addBufferLayout(0, pBufLayout); + + Vao::BufferVec buffers{ pVB }; + pVao = Vao::create(Vao::Topology::TriangleStrip, pLayout, buffers); + + if (!pVao || !pLayout || !pVB) throw std::exception(); + } + } + + FullScreenPass::FullScreenPass(const Program::Desc& progDesc, const Program::DefineList& programDefines) : BaseGraphicsPass(progDesc, programDefines) + { + gFullScreenData.objectCount++; + + // create depth stencil state + auto pDsState = DepthStencilState::create(DepthStencilState::Desc().setDepthEnabled(false)); + mpState->setDepthStencilState(pDsState); + + if (gFullScreenData.pVertexBuffer == nullptr) initFullScreenData(gFullScreenData.pVertexBuffer, gFullScreenData.pVao); + mpState->setVao(gFullScreenData.pVao); + + if (!mpState || !gFullScreenData.pVao) throw std::exception(); + } + + FullScreenPass::~FullScreenPass() + { + assert(gFullScreenData.objectCount > 0); + + gFullScreenData.objectCount--; + if (gFullScreenData.objectCount == 0) + { + gFullScreenData.pVao = nullptr; + gFullScreenData.pVertexBuffer = nullptr; + } + } + + FullScreenPass::SharedPtr FullScreenPass::create(const Program::Desc& desc, const Program::DefineList& defines, uint32_t viewportMask) + { + Program::Desc d = desc; + Program::DefineList defs = defines; + std::string gs; + + if (viewportMask) + { + defs.add("_VIEWPORT_MASK", std::to_string(viewportMask)); + if (checkForViewportArray2Support()) + { + defs.add("_USE_VP2_EXT"); + } + else + { + defs.add("_OUTPUT_VERTEX_COUNT", std::to_string(3 * popcount(viewportMask))); + d.addShaderLibrary("Framework/Shaders/FullScreenPass.gs.slang").gsEntry("main"); + } + } + if (d.getShaderEntryPoint(ShaderType::Vertex).empty()) d.addShaderLibrary("Framework/Shaders/FullScreenPass.vs.slang").vsEntry("main"); + + try + { + return SharedPtr(new FullScreenPass(d, defs)); + } + catch (std::exception) { return nullptr; } + } + + FullScreenPass::SharedPtr FullScreenPass::create(const std::string& psFile, const Program::DefineList& defines, uint32_t viewportMask) + { + Program::Desc d; + d.addShaderLibrary(psFile).psEntry("main"); + return create(d, defines, viewportMask); + } + + void FullScreenPass::execute(RenderContext* pRenderContext, const Fbo::SharedPtr& pFbo, bool autoSetVpSc) const + { + mpState->setFbo(pFbo, autoSetVpSc); + pRenderContext->draw(mpState.get(), mpVars.get(), arraysize(kVertices), 0); + } +} diff --git a/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h b/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h new file mode 100644 index 000000000..b09f05306 --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/FullScreenPass.h @@ -0,0 +1,66 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "BaseGraphicsPass.h" +#include "../RenderPass.h" + +namespace Falcor +{ + class dlldecl FullScreenPass : public BaseGraphicsPass, public std::enable_shared_from_this + { + public: + using SharedPtr = VarsSharedPtr; + + virtual ~FullScreenPass(); + + /** Create a new object from a pixel-shader + \param[in] filename Pixel shader filename. This method expects a pixel-shader named "main()" in the file + \param[in] defines Optional. A list of macro definitions to be patched into the shaders. + \param[in] viewportMask Optional. Value to initialize viewport mask with. Useful for multi-projection passes + */ + static SharedPtr create(const std::string& psFile, const Program::DefineList& defines = Program::DefineList(), uint32_t viewportMask = 0); + + /** Create a new object + \param[in] desc The program's description + \param[in] defines Optional. A list of macro definitions to be patched into the shaders. + \param[in] viewportMask Optional. Value to initialize viewport mask with. Useful for multi-projection passes + */ + static SharedPtr create(const Program::Desc& desc, const Program::DefineList& defines = Program::DefineList(), uint32_t viewportMask = 0); + + /** Execute the pass using an FBO + \param[in] pRenderContext The render context. + \param[in] pFbo The target FBO + \param[in] autoSetVpSc If true, the pass will set the viewports and scissors to match the FBO size. If you want to override the VP or SC, get the state by calling `getState()`, bind the SC and VP yourself and set this arg to false + */ + virtual void execute(RenderContext* pRenderContext, const Fbo::SharedPtr& pFbo, bool autoSetVpSc = true) const; + + protected: + FullScreenPass(const Program::Desc& progDesc, const Program::DefineList& programDefines); + }; +} + diff --git a/Framework/Source/ShadingUtils/ShadingUtilsTests.cs.hlsl b/Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp similarity index 57% rename from Framework/Source/ShadingUtils/ShadingUtilsTests.cs.hlsl rename to Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp index 481260734..22154d541 100644 --- a/Framework/Source/ShadingUtils/ShadingUtilsTests.cs.hlsl +++ b/Source/Falcor/RenderGraph/BasePasses/RasterPass.cpp @@ -25,49 +25,38 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" +#include "RasterPass.h" +#include "Core/API/RenderContext.h" -__import Helpers; - -RWStructuredBuffer result; - -cbuffer TestCB +namespace Falcor { - int resultSize; -}; + RasterPass::SharedPtr RasterPass::create(const Program::Desc& desc, const Program::DefineList& defines) + { + try + { + return SharedPtr(new RasterPass(desc, defines)); + } + catch (std::exception) { return nullptr; } + } -void testRadicalInverse() -{ - for (int i = 0; i < resultSize; ++i) + RasterPass::SharedPtr RasterPass::create(const std::string& filename, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& defines) { - result[i] = radicalInverse(i); + Program::Desc d; + d.addShaderLibrary(filename).vsEntry(vsEntry).psEntry(psEntry); + return create(d, defines); } -} -[numthreads(1024, 1, 1)] -void testRand(uint groupIndex : SV_GroupIndex) -{ - uint state = rand_init(0xd00f1337, groupIndex); - /* We intentionally have a relatively small number of threads, each of - which generates a bunch of random numbers, rather than having a - separate thread for each one. Doing so lets us have a large number - of successive samples from a relatively small number of random - contxts; otherwise, we'd basically only be testing the quality of - the initial seeding. - */ - int n = resultSize / 1024; - for (int i = 0; i < n; ++i) + RasterPass::RasterPass(const Program::Desc& progDesc, const Program::DefineList& programDefines) : + BaseGraphicsPass(progDesc, programDefines) {} + + void RasterPass::drawIndexed(RenderContext* pContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation) { - result[n * groupIndex + i] = rand_next(state); + pContext->drawIndexed(mpState.get(), mpVars.get(), indexCount, startIndexLocation, baseVertexLocation); } -} -[numthreads(1024, 1, 1)] -void testSphericalCoordinates(uint threadId : SV_DispatchThreadID) -{ - uint state = rand_init(0xd00f1337, threadId.x); - float3 dir = float3(-1 + 2 * rand_next(state), - -1 + 2 * rand_next(state), - -1 + 2 * rand_next(state)); - float2 uv = dirToSphericalCrd(dir); - result[threadId.x] = dot(normalize(dir), sphericalCrdToDir(uv)); + void RasterPass::draw(RenderContext* pContext, uint32_t vertexCount, uint32_t startVertexLocation) + { + pContext->draw(mpState.get(), mpVars.get(), vertexCount, startVertexLocation); + } } diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterPass.h b/Source/Falcor/RenderGraph/BasePasses/RasterPass.h new file mode 100644 index 000000000..e893119f8 --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/RasterPass.h @@ -0,0 +1,75 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core\Program\ProgramVarsHelpers.h" +#include "BaseGraphicsPass.h" + +namespace Falcor +{ + class dlldecl RasterPass : public BaseGraphicsPass, public std::enable_shared_from_this + { + public: + using SharedPtr = VarsSharedPtr; + + /** Create a new object. + \param[in] filename Shaders filename. + \param[in] vsEntry Vertex-shader entry point. If this string is empty (""), it will use a default vertex shader which transforms and outputs all default vertex attributes. + \param[in] psEntry Pixel shader entry point + \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. + \return A new object, or nullptr if creation failed. + */ + static SharedPtr create(const std::string& filename, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& defines = Program::DefineList()); + + /** Create a new object. + \param[in] progDesc The program's desc + \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. + \return A new object, or nullptr if creation failed. + */ + static SharedPtr create(const Program::Desc& desc, const Program::DefineList& defines = Program::DefineList()); + + /** Ordered draw call. + \param[in] vertexCount Number of vertices to draw + \param[in] startVertexLocation The location of the first vertex to read from the vertex buffers (offset in vertices) + */ + void draw(RenderContext* pContext, uint32_t vertexCount, uint32_t startVertexLocation); + + /** Indexed draw call. + \param[in] indexCount Number of indices to draw + \param[in] startIndexLocation The location of the first index to read from the index buffer (offset in indices) + \param[in] baseVertexLocation A value which is added to each index before reading a vertex from the vertex buffer + */ + void drawIndexed(RenderContext* pContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation); + protected: + /** Create a new object. + \param[in] progDesc The program's desc + \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. + \return A new object, or nullptr if creation failed. + */ + RasterPass(const Program::Desc& progDesc, const Program::DefineList& programDefines = Program::DefineList()); + }; +} diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp b/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp new file mode 100644 index 000000000..f547c164e --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "RasterScenePass.h" + +namespace Falcor +{ + RasterScenePass::RasterScenePass(const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines) : + BaseGraphicsPass(progDesc, programDefines), mpScene(pScene) + { + assert(pScene); + } + + RasterScenePass::SharedPtr RasterScenePass::create(const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines) + { + Program::DefineList dl = programDefines; + dl.add(pScene->getSceneDefines()); + + try + { + return SharedPtr(new RasterScenePass(pScene, progDesc, dl)); + } + catch (std::exception) { return nullptr; } + } + + RasterScenePass::SharedPtr RasterScenePass::create(const Scene::SharedPtr& pScene, const std::string& shaderFilename, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& programDefines) + { + Program::Desc d; + d.addShaderLibrary(shaderFilename).vsEntry(vsEntry).psEntry(psEntry); + return create(pScene, d, programDefines); + } + + void RasterScenePass::renderScene(RenderContext* pContext, const Fbo::SharedPtr& pDstFbo) + { + mpState->setFbo(pDstFbo); + mpScene->render(pContext, mpState.get(), mpVars.get()); + } + + bool RasterScenePass::onMouseEvent(const MouseEvent& mouseEvent) + { + return mpScene->onMouseEvent(mouseEvent); + } + + bool RasterScenePass::onKeyEvent(const KeyboardEvent& keyEvent) + { + return mpScene->onKeyEvent(keyEvent); + } +} diff --git a/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h b/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h new file mode 100644 index 000000000..04b75bc78 --- /dev/null +++ b/Source/Falcor/RenderGraph/BasePasses/RasterScenePass.h @@ -0,0 +1,77 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "BaseGraphicsPass.h" +#include "../RenderPass.h" + +namespace Falcor +{ + class dlldecl RasterScenePass : public BaseGraphicsPass, public std::enable_shared_from_this + { + public: + using SharedPtr = VarsSharedPtr::shared_ptr; + + /** Create a new object. + \param[in] pScene The scene object + \param[in] progDesc The program's desc + \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. + \return A new object, or nullptr if creation failed. + */ + static SharedPtr create(const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines = Program::DefineList()); + + /** Create a new object. + \param[in] pScene The scene object + \param[in] filename Shaders filename. + \param[in] vsEntry Vertex-shader entry point. If this string is empty (""), it will use a default vertex shader which transforms and outputs all default vertex attributes. + \param[in] psEntry Pixel shader entry point + \param[in] programDefines A list of macro definitions to set into the shaders. The macro definitions will be assigned to all the shaders. + \return A new object, or nullptr if creation failed. + */ + static SharedPtr create(const Scene::SharedPtr& pScene, const std::string& shaderFilename, const std::string& vsEntry, const std::string& psEntry, const Program::DefineList& programDefines = Program::DefineList()); + + /** Render the scene into the dst FBO + */ + void renderScene(RenderContext* pContext, const Fbo::SharedPtr& pDstFbo); + + /** Call whenever a keyboard event happens + */ + bool onKeyEvent(const KeyboardEvent& keyEvent); + + /** Call whenever a mouse event happened + */ + bool onMouseEvent(const MouseEvent& mouseEvent); + + /** Get the scene + */ + const Scene::SharedPtr& getScene() const { return mpScene; } + private: + RasterScenePass(const Scene::SharedPtr& pScene, const Program::Desc& progDesc, const Program::DefineList& programDefines = Program::DefineList()); + Scene::SharedPtr mpScene; + }; +} + diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraph.cpp b/Source/Falcor/RenderGraph/RenderGraph.cpp similarity index 53% rename from Framework/Source/Experimental/RenderGraph/RenderGraph.cpp rename to Source/Falcor/RenderGraph/RenderGraph.cpp index c776db1ae..ed4374085 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraph.cpp +++ b/Source/Falcor/RenderGraph/RenderGraph.cpp @@ -25,17 +25,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RenderGraph.h" -#include "API/FBO.h" -#include "Utils/DirectedGraphTraversal.h" -#include "Utils/Gui.h" -#include "Graphics/Scene/Scene.h" -#include "Experimental/RenderGraph/RenderPassLibrary.h" -#include "Experimental/RenderPasses/ResolvePass.h" +#include "RenderPassLibrary.h" +#include "Utils/Algorithm/DirectedGraphTraversal.h" +#include "RenderGraphCompiler.h" namespace Falcor { + std::vector gRenderGraphs; const FileDialogFilterVec RenderGraph::kFileExtensionFilters = { { "py", "Render Graph Files"} }; RenderGraph::SharedPtr RenderGraph::create(const std::string& name) @@ -44,8 +42,9 @@ namespace Falcor { return SharedPtr(new RenderGraph(name)); } - catch (const std::exception&) + catch (const std::exception& e) { + logError(std::string("Can't create a new RenderGraph. ") + e.what()); return nullptr; } } @@ -53,9 +52,9 @@ namespace Falcor RenderGraph::RenderGraph(const std::string& name) : mName(name) { mpGraph = DirectedGraph::create(); - mpResourcesCache = ResourceCache::create(); mpPassDictionary = Dictionary::create(); gRenderGraphs.push_back(this); + onResize(gpFramework->getTargetFbo().get()); } RenderGraph::~RenderGraph() @@ -71,22 +70,26 @@ namespace Falcor return (it == mNameToIndex.end()) ? kInvalidIndex : it->second; } - void RenderGraph::setScene(const std::shared_ptr& pScene) + void RenderGraph::setScene(const Scene::SharedPtr& pScene) { + if (mpScene == pScene) return; + mpScene = pScene; for (auto& it : mNodeData) { - it.second.pPass->setScene(pScene); + it.second.pPass->setScene(gpDevice->getRenderContext(), pScene); } + mRecompile = true; } - uint32_t RenderGraph::addPass(const RenderPass::SharedPtr& pPass, const std::string& passName, PassFlags passFlags) + uint32_t RenderGraph::addPass(const RenderPass::SharedPtr& pPass, const std::string& passName) { assert(pPass); uint32_t passIndex = getPassIndex(passName); if (passIndex != kInvalidIndex) { - logWarning("Pass named `" + passName + "' already exists. Replacing existing pass"); + logError("Pass named `" + passName + "' already exists. Ignoring call"); + return kInvalidIndex; } else { @@ -94,10 +97,11 @@ namespace Falcor mNameToIndex[passName] = passIndex; } - auto passChangedCB = [this]() {mRecompile = true; }; - pPass->setPassChangedCB(passChangedCB); - pPass->setScene(mpScene); - mNodeData[passIndex] = { passName, pPass, passFlags }; + pPass->mPassChangedCB = [this]() { mRecompile = true; }; + pPass->mName = passName; + + if(mpScene) pPass->setScene(gpDevice->getRenderContext(), mpScene); + mNodeData[passIndex] = { passName, pPass }; mRecompile = true; return passIndex; } @@ -119,45 +123,36 @@ namespace Falcor { if (o.nodeId == index) outputsToDelete.push_back(outputPrefix + o.field); } - - for (const auto& name : outputsToDelete) - { - unmarkOutput(name); - } - // Update the indices + // Remove all the edges, indices and pass-data associated with this pass + for (const auto& name : outputsToDelete) unmarkOutput(name); mNameToIndex.erase(name); - - // remove pass data mNodeData.erase(index); - - // Remove all the edges associated with this pass const auto& removedEdges = mpGraph->removeNode(index); for (const auto& e : removedEdges) mEdgeData.erase(e); - mRecompile = true; } - void RenderGraph::updatePass(const std::string& passName, const Dictionary& dict) + void RenderGraph::updatePass(RenderContext* pRenderContext, const std::string& passName, const Dictionary& dict) { uint32_t index = getPassIndex(passName); const auto pPassIt = mNodeData.find(index); if (pPassIt == mNodeData.end()) { - logWarning("Unable to find pass " + passName + "."); + logError("Error in RenderGraph::updatePass(). Unable to find pass " + passName); + return; } - // recreate pass without changing graph using new dictionary + // Recreate pass without changing graph using new dictionary auto pOldPass = pPassIt->second.pPass; - std::string passTypeName = pOldPass->getName(); - auto pPass = RenderPassLibrary::instance().createPass(passTypeName.c_str(), dict); + std::string passTypeName = getClassTypeName(pOldPass.get()); + auto pPass = RenderPassLibrary::instance().createPass(pRenderContext, passTypeName.c_str(), dict); pPassIt->second.pPass = pPass; + pPass->mPassChangedCB = [this]() { mRecompile = true; }; + pPass->mName = pOldPass->getName(); - auto passChangedCB = [this]() {mRecompile = true; }; - pPass->setPassChangedCB(passChangedCB); - - pPass->setScene(mpScene); + pPass->setScene(gpDevice->getRenderContext(), mpScene); mRecompile = true; } @@ -167,7 +162,7 @@ namespace Falcor if (index == kInvalidIndex) { static RenderPass::SharedPtr pNull; - logWarning("RenderGraph::getRenderPass() - can't find a pass named `" + name + "`"); + logError("RenderGraph::getRenderPass() - can't find a pass named `" + name + "`"); return pNull; } return mNodeData.at(index).pPass; @@ -176,12 +171,12 @@ namespace Falcor using str_pair = std::pair; template - static bool checkRenderPassIoExist(const RenderPass* pPass, const std::string& name) + static bool checkRenderPassIoExist(RenderPass* pPass, const std::string& name) { - RenderPassReflection reflect = pPass->reflect(); + RenderPassReflection reflect = pPass->reflect({}); for (size_t i = 0; i < reflect.getFieldCount(); i++) { - const auto& f = reflect.getField(i); + const auto& f = *reflect.getField(i); if (f.getName() == name) { return input ? is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Input) : is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Output); @@ -283,7 +278,7 @@ namespace Falcor // Make sure that this doesn't create a cycle if (DirectedGraphPathDetector::hasPath(mpGraph, dstIndex, srcIndex)) { - logError("RenderGraph::addEdge() - can't add the edge [" + src + ", " + dst + "]. This will create a cycle in the graph which is not allowed"); + logError("RenderGraph::addEdge() - can't add the edge [" + src + ", " + dst + "]. The edge will create a cycle in the graph which is not allowed"); return kInvalidIndex; } @@ -301,7 +296,7 @@ namespace Falcor if (pSrc == nullptr || pDst == nullptr) { - logWarning("Unable to remove edge. Input or output node not found."); + logError("Unable to remove edge. Input or output node not found."); return; } @@ -325,6 +320,11 @@ namespace Falcor void RenderGraph::removeEdge(uint32_t edgeID) { + if (mEdgeData.find(edgeID) == mEdgeData.end()) + { + logError("Can't remove edge with index " + std::to_string(edgeID) + ". The edge doesn't exist"); + return; + } mEdgeData.erase(edgeID); mpGraph->removeEdge(edgeID); mRecompile = true; @@ -340,200 +340,16 @@ namespace Falcor if (!mpGraph->doesEdgeExist(i)) { continue; } const DirectedGraph::Edge* pEdge = mpGraph->getEdge(i); - if (dstPair.first == mNodeData[pEdge->getDestNode()].nodeName && - srcPair.first == mNodeData[pEdge->getSourceNode()].nodeName) + if (dstPair.first == mNodeData[pEdge->getDestNode()].name && + srcPair.first == mNodeData[pEdge->getSourceNode()].name) { - if (mEdgeData[i].dstField == dstPair.second - && mEdgeData[i].srcField == srcPair.second) - { - return i; - } + if (mEdgeData[i].dstField == dstPair.second && mEdgeData[i].srcField == srcPair.second) return i; } } return static_cast(-1); } - // MATT TODO - bool RenderGraph::isValid(std::string& log) const - { - std::vector nodeVec; - - for (uint32_t i = 0; i < mpGraph->getCurrentNodeId(); i++) - { - if (mpGraph->doesNodeExist(i)) - { - const auto& nodeIt = mNodeData.find(i); - nodeVec.push_back(&nodeIt->second); - } - } - - for (const NodeData* pNodeData : nodeVec) - { - RenderPassReflection passReflection = pNodeData->pPass->reflect(); - - uint32_t numRequiredInputs = 0; - bool hasGraphOutput = false; - const DirectedGraph::Node* pNode = mpGraph->getNode(mNameToIndex.at(pNodeData->nodeName)); - - // get input count - for (uint32_t i = 0; i < passReflection.getFieldCount(); ++i) - { - const RenderPassReflection::Field& field = passReflection.getField(i); - - if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input)) - { - if (!is_set(field.getFlags(), RenderPassReflection::Field::Flags::Optional)) numRequiredInputs++; - } - - GraphOut graphOut; - graphOut.field = field.getName(); - graphOut.nodeId = getPassIndex(pNodeData->nodeName); - hasGraphOutput |= isGraphOutput(graphOut); - } - - // check if node has no inputs, and has connected outgoing edges - bool hasOutputs = (pNode->getOutgoingEdgeCount() || hasGraphOutput); - if (hasOutputs && (numRequiredInputs > pNode->getIncomingEdgeCount()) ) - { - return false; - } - } - - return true; - } - - bool RenderGraph::resolveExecutionOrder() - { - mExecutionList.clear(); - - // Find out which passes are mandatory - std::unordered_set mandatoryPasses; - for (auto& o : mOutputs) mandatoryPasses.insert(o.nodeId); // Add direct-graph outputs - for (auto& n : mNodeData) if (is_set(n.second.passFlags, PassFlags::ForceExecution)) mandatoryPasses.insert(n.first); // Add the force-execution passes - - for (auto& e : mEdgeData) // Add all the passes which have an execution-edge connected to them - { - if (e.second.dstField.empty()) - { - assert(e.second.srcField.empty()); - const auto& edge = mpGraph->getEdge(e.first); - mandatoryPasses.insert(edge->getDestNode()); - mandatoryPasses.insert(edge->getSourceNode()); - } - } - - // Find all passes that affect the outputs - std::unordered_set participatingPasses; - for (auto& o : mandatoryPasses) - { - uint32_t nodeId = o; - auto dfs = DirectedGraphDfsTraversal(mpGraph, nodeId, DirectedGraphDfsTraversal::Flags::IgnoreVisited | DirectedGraphDfsTraversal::Flags::Reverse); - while (nodeId != DirectedGraph::kInvalidID) - { - participatingPasses.insert(nodeId); - nodeId = dfs.traverse(); - } - } - - // Run topological sort - auto topologicalSort = DirectedGraphTopologicalSort::sort(mpGraph.get()); - - // For each object in the vector, if it's being used in the execution, put it in the list - for (auto& node : topologicalSort) - { - if (participatingPasses.find(node) != participatingPasses.end()) - { - mExecutionList.push_back(node); - } - } - - return true; - } - - bool RenderGraph::insertAutoPasses() - { - bool addedPasses = false; - for (size_t i = 0; i < mExecutionList.size(); i++) - { - uint32_t nodeIndex = mExecutionList[i]; - const DirectedGraph::Node* pNode = mpGraph->getNode(nodeIndex); - assert(pNode); - RenderPass* pSrcPass = mNodeData[nodeIndex].pPass.get(); - RenderPassReflection passReflection = pSrcPass->reflect(); - - // Check for opportunities to automatically resolve MSAA - // - Only take explicitly specified MS output - for (uint32_t f = 0; f < passReflection.getFieldCount(); f++) - { - // Iterate over output fields - auto& srcField = passReflection.getField(f); - if (is_set(srcField.getVisibility(), RenderPassReflection::Field::Visibility::Output) == false) continue; - - const std::string& srcPassName = mNodeData[nodeIndex].nodeName; - // Gather src field name, and every input it is connected to - std::string srcFieldName = srcPassName + '.' + srcField.getName(); - std::vector dstFieldNames; - - for (uint32_t e = 0; e < pNode->getOutgoingEdgeCount(); e++) - { - uint32_t edgeIndex = pNode->getOutgoingEdge(e); - const auto& edgeData = mEdgeData[edgeIndex]; - - // For every output field, iterate over all edges extending from that field - if (srcField.getName() == edgeData.srcField) - { - const auto& pEdge = mpGraph->getEdge(edgeIndex); - const std::string& dstPassName = mNodeData[pEdge->getDestNode()].nodeName; - - // If edge is connected to something that isn't executed, ignore - if (std::find(mExecutionList.begin(), mExecutionList.end(), pEdge->getDestNode()) == mExecutionList.end()) continue; - - RenderPassReflection dstReflection = mNodeData[pEdge->getDestNode()].pPass->reflect(); - const auto& dstField = dstReflection.getField(edgeData.dstField); - - assert(srcField.isValid() && dstField.isValid()); - if (canAutoResolve(srcField, dstField)) - { - std::string dstFieldName = dstPassName + '.' + dstField.getName(); - dstFieldNames.push_back(dstFieldName); - } - } - } - - // If there are connections to add MSAA Resolve - if (dstFieldNames.size() > 0) - { - // One resolve pass is made for every output that requires it - auto pResolvePass = std::static_pointer_cast(RenderPassLibrary::instance().createPass("ResolvePass")); - pResolvePass->setFormat(srcField.getFormat()); // Match input texture format - - // Create pass and attach src to it - std::string resolvePassName = srcFieldName + "-ResolvePass"; - addPass(pResolvePass, resolvePassName); - addEdge(srcFieldName, resolvePassName + ".src"); - - // For every input the src field is connected to, connect the resolve pass output to the input - for (const auto& dstFieldName : dstFieldNames) - { - // Remove original edge - removeEdge(srcFieldName, dstFieldName); - // Replace with edge coming from resolve output - addEdge(resolvePassName + ".dst", dstFieldName); - - // Log changes made to user's graph by compilation process - mCompilationChanges.removedEdges.emplace_back(srcFieldName, dstFieldName); - } - - mCompilationChanges.generatedPasses.push_back(resolvePassName); - addedPasses = true; - } - } - } - - return addedPasses; - } - bool RenderGraph::isGraphOutput(const GraphOut& graphOut) const { for (const GraphOut& currentOut : mOutputs) @@ -547,131 +363,54 @@ namespace Falcor std::vector RenderGraph::getAvailableOutputs() const { std::vector outputs; + for (const auto& node : mNodeData) { - RenderPassReflection reflection = node.second.pPass->reflect(); + RenderPassReflection reflection = node.second.pPass->reflect({}); for (size_t i = 0; i < reflection.getFieldCount(); i++) { - const auto& f = reflection.getField(i); - if(is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Output)) outputs.push_back(node.second.nodeName + "." + f.getName()); + const auto& f = *reflection.getField(i); + if(is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Output)) outputs.push_back(node.second.name + "." + f.getName()); } } return outputs; } - bool RenderGraph::resolveResourceTypes() + bool RenderGraph::compile(RenderContext* pContext, std::string& log) { - // Build list to look up execution order index from the pass - std::unordered_map passToIndex; - for (size_t i = 0; i < mExecutionList.size(); i++) - { - passToIndex.emplace(mNodeData[mExecutionList[i]].pPass.get(), uint32_t(i)); - } + if (!mRecompile) return true; + mpExe = nullptr; - for (size_t i = 0; i < mExecutionList.size(); i++) + try { - uint32_t nodeIndex = mExecutionList[i]; - const DirectedGraph::Node* pNode = mpGraph->getNode(nodeIndex); - assert(pNode); - RenderPass* pCurrPass = mNodeData[nodeIndex].pPass.get(); - RenderPassReflection passReflection = pCurrPass->reflect(); - - const auto isGraphOutput = [=](uint32_t nodeId, const std::string& field) - { - for (const auto& out : mOutputs) - { - if (out.nodeId == nodeId && out.field == field) return true; - } - return false; - }; - - // Register all pass outputs - for (size_t f = 0; f < passReflection.getFieldCount(); f++) - { - const auto& field = passReflection.getField(f); - std::string fullFieldName = mNodeData[nodeIndex].nodeName + '.' + field.getName(); - - // Skip input resources, we never allocate them - if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output | RenderPassReflection::Field::Visibility::Internal)) - { - mpResourcesCache->registerField(fullFieldName, field, uint32_t(i)); - - // Resource lifetime for graph outputs must extend to end of graph execution - if(isGraphOutput(nodeIndex, field.getName())) mpResourcesCache->registerField(fullFieldName, field, uint32_t(-1)); - } - } - - // Go over the pass inputs, add them as aliases to the outputs that connect to them (which should be already registered above) - for (uint32_t e = 0; e < pNode->getIncomingEdgeCount(); e++) - { - uint32_t edgeIndex = pNode->getIncomingEdge(e); - const auto& pEdge = mpGraph->getEdge(edgeIndex); - const auto& edgeData = mEdgeData[edgeIndex]; - - // Skip execution-edges - if (edgeData.dstField.empty()) - { - assert(edgeData.srcField.empty()); - continue; - } - - const auto& dstField = passReflection.getField(edgeData.dstField); - assert(dstField.isValid() && is_set(dstField.getVisibility(), RenderPassReflection::Field::Visibility::Input)); - - // Merge dst/input field into same resource data - std::string srcFieldName = mNodeData[pEdge->getSourceNode()].nodeName + '.' + edgeData.srcField; - std::string dstFieldName = mNodeData[nodeIndex].nodeName + '.' + dstField.getName(); - - const auto& pSrcPass = mNodeData[pEdge->getSourceNode()].pPass; - auto srcReflection = pSrcPass->reflect(); - assert(passToIndex.count(pSrcPass.get()) > 0); - mpResourcesCache->registerField(dstFieldName, dstField, passToIndex[pSrcPass.get()], srcFieldName); - } + mpExe = RenderGraphCompiler::compile(*this, pContext, mCompilerDeps); + mRecompile = false; + return true; } - - mpResourcesCache->allocateResources(mSwapChainData); - return true; - } - - bool RenderGraph::compile(std::string& log) - { - if (mRecompile) + catch (std::exception e) { - mpResourcesCache->reset(); - restoreCompilationChanges(); - - if (resolveExecutionOrder() == false) return false; - // If passes were added, resolve execution order again - if (insertAutoPasses()) if (resolveExecutionOrder() == false) return false; - if (resolveResourceTypes() == false) return false; - if (isValid(log) == false) return false; + log = e.what(); + return false; } - mRecompile = false; - return true; } void RenderGraph::execute(RenderContext* pContext) { - bool profile = mProfileGraph && gProfileEnabled; - - if (profile) Profiler::startEvent("RenderGraph::execute()"); - std::string log; - if (!compile(log)) + if (!compile(pContext, log)) { - logWarning("Failed to compile RenderGraph\n" + log + "Ignoring RenderGraph::execute() call"); + logError("Failed to compile RenderGraph\n" + log + "Ignoring RenderGraph::execute() call"); return; } - for (const auto& node : mExecutionList) - { - if (profile) Profiler::startEvent(mNodeData[node].nodeName); - RenderData renderData(mNodeData[node].nodeName, mpResourcesCache, mpPassDictionary); - mNodeData[node].pPass->execute(pContext, &renderData); - if (profile) Profiler::endEvent(mNodeData[node].nodeName); - } - - if (profile) Profiler::endEvent("RenderGraph::execute()"); + assert(mpExe); + RenderGraphExe::Context c; + c.pGraphDictionary = mpPassDictionary; + c.pRenderContext = pContext; + c.profileGraph = mProfileGraph; + c.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; + c.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; + mpExe->execute(c); } void RenderGraph::update(const SharedPtr& pGraph) @@ -681,13 +420,7 @@ namespace Falcor { // if same name and type RenderPass::SharedPtr pRenderPass = pGraph->mNodeData[nameIndexPair.second].pPass; - PassFlags flags = pGraph->mNodeData[nameIndexPair.second].passFlags; - std::string passTypeName = pRenderPass->getName(); - - if (!doesPassExist(nameIndexPair.first) || mNodeData[nameIndexPair.second].passFlags != flags) - { - addPass(pRenderPass, nameIndexPair.first, flags); - } + if (!doesPassExist(nameIndexPair.first)) addPass(pRenderPass, nameIndexPair.first); } std::vector passesToRemove; @@ -721,8 +454,8 @@ namespace Falcor if (!pGraph->mpGraph->doesEdgeExist(i)) { continue; } const DirectedGraph::Edge* pEdge = pGraph->mpGraph->getEdge(i); - std::string dst = pGraph->mNodeData.find(pEdge->getDestNode())->second.nodeName; - std::string src = pGraph->mNodeData.find(pEdge->getSourceNode())->second.nodeName; + std::string dst = pGraph->mNodeData.find(pEdge->getDestNode())->second.name; + std::string src = pGraph->mNodeData.find(pEdge->getSourceNode())->second.name; if ((mNameToIndex.find(src) != mNameToIndex.end()) && (mNameToIndex.find(dst) != mNameToIndex.end())) { @@ -737,17 +470,35 @@ namespace Falcor for (uint32_t i = 0; i < pGraph->getOutputCount(); ++i) { markOutput(pGraph->getOutputName(i)); } } - bool RenderGraph::setInput(const std::string& name, const std::shared_ptr& pResource) + void RenderGraph::setInput(const std::string& name, const Resource::SharedPtr& pResource) { str_pair strPair; RenderPass* pPass = getRenderPassAndNamePair(this, name, "RenderGraph::setInput()", strPair); - if (pPass == nullptr) return false; - mpResourcesCache->registerExternalInput(name, pResource); - return true; + if (pPass == nullptr) return; + + if (pResource) mCompilerDeps.externalResources[name] = pResource; + else + { + if (mCompilerDeps.externalResources.find(name) == mCompilerDeps.externalResources.end()) + { + logWarning("RenderGraph::setInput() - Trying to remove an external resource named `" + name + "` but the resource wasn't registered before. Ignoring call"); + return; + } + mCompilerDeps.externalResources.erase(name); + } + + if (mpExe) mpExe->setInput(name, pResource); } void RenderGraph::markOutput(const std::string& name) { + if (name == "*") + { + auto outputs = getAvailableOutputs(); + for (const auto& o : outputs) markOutput(o); + return; + } + str_pair strPair; const auto& pPass = getRenderPassAndNamePair(this, name, "RenderGraph::markGraphOutput()", strPair); if (pPass == nullptr) return; @@ -787,24 +538,51 @@ namespace Falcor } } - const Resource::SharedPtr RenderGraph::getOutput(const std::string& name) + bool RenderGraph::isGraphOutput(const std::string& name) { - static const Resource::SharedPtr pNull; + str_pair strPair; + const auto& pPass = getRenderPassAndNamePair(this, name, "RenderGraph::unmarkGraphOutput()", strPair); + if (pPass == nullptr) return false; + uint32_t passIndex = getPassIndex(strPair.first); + GraphOut thisOutput = { passIndex, strPair.second }; + return isGraphOutput(thisOutput); + } + + Resource::SharedPtr RenderGraph::getOutput(const std::string& name) + { + if (mRecompile) + { + logError("RenderGraph::getOutput() - can't fetch an output resource because the graph wasn't successfuly compiled yet"); + return nullptr; + } + str_pair strPair; RenderPass* pPass = getRenderPassAndNamePair(this, name, "RenderGraph::getOutput()", strPair); + if (!pPass) return nullptr; + uint32_t passIndex = getPassIndex(strPair.first); GraphOut thisOutput = { passIndex, strPair.second }; bool isOutput = isGraphOutput(thisOutput); - auto pRes = (pPass && isOutput) ? mpResourcesCache->getResource(name) : pNull; - assert(pRes); - return pRes; + if (!isOutput) + { + logError("RenderGraph::getOutput() - can't fetch the output `" + name + "`. The resource is wasn't marked as an output"); + return nullptr; + } + + return mpExe->getResource(name); + } + + Resource::SharedPtr RenderGraph::getOutput(uint32_t index) + { + auto name = getOutputName(index); + return getOutput(name); } std::string RenderGraph::getOutputName(size_t index) const { assert(index < mOutputs.size()); const GraphOut& graphOut = mOutputs[index]; - return mNodeData.find(graphOut.nodeId)->second.nodeName + "." + graphOut.field; + return mNodeData.find(graphOut.nodeId)->second.name + "." + graphOut.field; } void RenderGraph::onResize(const Fbo* pTargetFbo) @@ -814,21 +592,9 @@ namespace Falcor const Texture* pDepth = pTargetFbo->getDepthStencilTexture().get(); assert(pColor && pDepth); - // If the back-buffer values changed, recompile - mRecompile = mRecompile || (mSwapChainData.format != pColor->getFormat()); - mRecompile = mRecompile || (mSwapChainData.width != pTargetFbo->getWidth()); - mRecompile = mRecompile || (mSwapChainData.height != pTargetFbo->getHeight()); - // Store the values - mSwapChainData.format = pColor->getFormat(); - mSwapChainData.width = pTargetFbo->getWidth(); - mSwapChainData.height = pTargetFbo->getHeight(); - - // Invoke the passes' callback - for (const auto& it : mNodeData) - { - it.second.pPass->onResize(mSwapChainData.width, mSwapChainData.height); - } + mCompilerDeps.defaultResourceProps.format = pColor->getFormat(); + mCompilerDeps.defaultResourceProps.dims = { pTargetFbo->getWidth(), pTargetFbo->getHeight() }; // Invalidate the graph. Render-passes might change their reflection based on the resize information mRecompile = true; @@ -860,19 +626,17 @@ namespace Falcor // For every output in src pass for (uint32_t i = 0; i < srcReflection.getFieldCount(); i++) { - const RenderPassReflection::Field& srcField = srcReflection.getField(i); + const RenderPassReflection::Field& srcField = *srcReflection.getField(i); if (is_set(srcField.getVisibility(), RenderPassReflection::Field::Visibility::Output) && canFieldsConnect(srcField, *dstFieldIt)) { // Add Edge - uint32_t srcIndex = mNameToIndex[pSrcNode->nodeName]; - uint32_t dstIndex = mNameToIndex[pdstNode->nodeName]; + uint32_t srcIndex = mNameToIndex[pSrcNode->name]; + uint32_t dstIndex = mNameToIndex[pdstNode->name]; uint32_t e = mpGraph->addEdge(srcIndex, dstIndex); mEdgeData[e] = { true, srcField.getName(), dstFieldIt->getName() }; mRecompile = true; - - // If connection was found, continue to next unsatisfied input - inputSatisfied = true; + inputSatisfied = true; // If connection was found, continue to next unsatisfied input break; } } @@ -882,27 +646,13 @@ namespace Falcor } } - bool RenderGraph::canAutoResolve(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst) - { - return src.getSampleCount() > 1 && dst.getSampleCount() == 1; - } - - void RenderGraph::restoreCompilationChanges() - { - for (const auto& name : mCompilationChanges.generatedPasses) removePass(name); - for (const auto& e : mCompilationChanges.removedEdges) addEdge(e.first, e.second); - - mCompilationChanges.generatedPasses.clear(); - mCompilationChanges.removedEdges.clear(); - } - void RenderGraph::getUnsatisfiedInputs(const NodeData* pNodeData, const RenderPassReflection& passReflection, std::vector& outList) const { - assert(mNameToIndex.count(pNodeData->nodeName) > 0); + assert(mNameToIndex.count(pNodeData->name) > 0); // Get names of connected input edges std::vector satisfiedFields; - const DirectedGraph::Node* pNode = mpGraph->getNode(mNameToIndex.at(pNodeData->nodeName)); + const DirectedGraph::Node* pNode = mpGraph->getNode(mNameToIndex.at(pNodeData->name)); for (uint32_t i = 0; i < pNode->getIncomingEdgeCount(); i++) { const auto& edgeData = mEdgeData.at(pNode->getIncomingEdge(i)); @@ -912,12 +662,12 @@ namespace Falcor // Build list of unsatisfied fields by comparing names with which edges/fields are connected for (uint32_t i = 0; i < passReflection.getFieldCount(); i++) { - const RenderPassReflection::Field& field = passReflection.getField(i); + const RenderPassReflection::Field& field = *passReflection.getField(i); bool isUnsatisfied = std::find(satisfiedFields.begin(), satisfiedFields.end(), field.getName()) == satisfiedFields.end(); if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input) && isUnsatisfied) { - outList.push_back(passReflection.getField(i)); + outList.push_back(*passReflection.getField(i)); } } } @@ -974,61 +724,73 @@ namespace Falcor } } - void RenderGraph::renderUI(Gui* pGui, const char* uiGroup) + void RenderGraph::renderUI(Gui::Widgets& widget) { - if (!uiGroup || pGui->beginGroup(uiGroup, true)) + if (mpScene) { - if (mpScene) + auto sceneGroup = Gui::Group(widget, "Scene Settings"); + if (sceneGroup.open()) { - if(pGui->beginGroup("Scene Settings")) - { - mpScene->renderUI(pGui); - pGui->endGroup(); - } - pGui->addSeparator(); - } - - pGui->addCheckBox("Profile Passes", mProfileGraph); - pGui->addTooltip("Profile the render-passes. The results will be shown in the profiler window. If you can't see it, click 'P'"); - - for (const auto& passId : mExecutionList) - { - const auto& pass = mNodeData[passId]; - - // If you are thinking about displaying the profiler results next to the group label, it won't work. Since the times change every frame, IMGUI thinks it's a different group and will not expand it - bool groupOpen = pGui->beginGroup(pass.nodeName); - const auto& desc = pass.pPass->getDesc(); - if (desc.size()) pGui->addTooltip(desc.c_str()); - if (groupOpen) - { - pass.pPass->renderUI(pGui, nullptr); - pGui->endGroup(); - } + mpScene->renderUI(sceneGroup); + sceneGroup.release(); } - - if (uiGroup) pGui->endGroup(); + + widget.separator(); } + + widget.checkbox("Profile Passes", mProfileGraph); + widget.tooltip("Profile the render-passes. The results will be shown in the profiler window. If you can't see it, click 'P'"); + if (mpExe) mpExe->renderUI(widget); } bool RenderGraph::onMouseEvent(const MouseEvent& mouseEvent) { - bool b = false; - for (const auto& passId : mExecutionList) - { - const auto& pPass = mNodeData[passId].pPass; - b = b || pPass->onMouseEvent(mouseEvent); - } - return b; + return mpExe ? mpExe->onMouseEvent(mouseEvent) : false; } bool RenderGraph::onKeyEvent(const KeyboardEvent& keyEvent) { - bool b = false; - for (const auto& passId : mExecutionList) + return mpExe ? mpExe->onKeyEvent(keyEvent) : false; + } + + SCRIPT_BINDING(RenderGraph) + { + void(RenderGraph::*renderGraphRemoveEdge)(const std::string&, const std::string&)(&RenderGraph::removeEdge); + auto graphClass = m.regClass(RenderGraph); + graphClass.ctor(&RenderGraph::create); + graphClass.func_(RenderGraphIR::kAddPass, &RenderGraph::addPass, "renderPass"_a, "passName"_a).func_(RenderGraphIR::kRemovePass, &RenderGraph::removePass); + graphClass.func_(RenderGraphIR::kAddEdge, &RenderGraph::addEdge).func_(RenderGraphIR::kRemoveEdge, renderGraphRemoveEdge); + graphClass.func_(RenderGraphIR::kMarkOutput, &RenderGraph::markOutput).func_(RenderGraphIR::kUnmarkOutput, &RenderGraph::unmarkOutput); + graphClass.func_(RenderGraphIR::kAutoGenEdges, &RenderGraph::autoGenEdges); + graphClass.func_("name", &RenderGraph::setName); + graphClass.func_("name", &RenderGraph::getName); + graphClass.func_("getPass", &RenderGraph::getPass); + auto printGraph = [](RenderGraph::SharedPtr pGraph) { pybind11::print(RenderGraphExporter::getIR(pGraph)); }; + graphClass.func_("print", printGraph); + graphClass.func_("getOutput", ScriptBindings::overload_cast(&RenderGraph::getOutput)); + + // RenderPass + auto passClass = m.regClass(RenderPass); + + // RenderPassLibrary + const auto& createRenderPass = [](const std::string& passName, pybind11::dict d = {}) { - const auto& pPass = mNodeData[passId].pPass; - b = b || pPass->onKeyEvent(keyEvent); - } - return b; + auto pPass = RenderPassLibrary::instance().createPass(gpDevice->getRenderContext(), passName.c_str(), Dictionary(d)); + if (!pPass) throw std::exception(("Can't create a render pass named `" + passName + "`. Make sure the required DLL was loaded").c_str()); + return pPass; + }; + passClass.ctor(createRenderPass, "passName"_a, "dict"_a = pybind11::dict()); + + const auto& loadPassLibrary = [](const std::string& library) + { + return RenderPassLibrary::instance().loadLibrary(library); + }; + m.func_(RenderGraphIR::kLoadPassLibrary, loadPassLibrary); + + const auto& updateRenderPass = [](const RenderGraph::SharedPtr& pGraph, const std::string& passName, pybind11::dict d) + { + pGraph->updatePass(gpDevice->getRenderContext(), passName, Dictionary(d)); + }; + graphClass.func_(RenderGraphIR::kUpdatePass, updateRenderPass); } } diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraph.h b/Source/Falcor/RenderGraph/RenderGraph.h similarity index 80% rename from Framework/Source/Experimental/RenderGraph/RenderGraph.h rename to Source/Falcor/RenderGraph/RenderGraph.h index 591ad2ed3..a9eccf354 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraph.h +++ b/Source/Falcor/RenderGraph/RenderGraph.h @@ -26,19 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "RenderPass.h" -#include "Utils/DirectedGraph.h" +#include "RenderPassReflection.h" #include "ResourceCache.h" +#include "Scene/Scene.h" +#include "RenderPass.h" +#include "Utils/Algorithm/DirectedGraph.h" +#include "RenderGraphExe.h" +#include "RenderGraphCompiler.h" namespace Falcor { - class Scene; - class Texture; - class Fbo; - class RenderGraphExporter; - class RenderPassLibrary; - - class RenderGraph + class dlldecl RenderGraph { public: using SharedPtr = std::shared_ptr; @@ -48,23 +46,17 @@ namespace Falcor ~RenderGraph(); - enum class PassFlags - { - None = 0x0, - ForceExecution = 0x1, - }; - /** Create a new object */ static SharedPtr create(const std::string& name = ""); /** Set a scene */ - void setScene(const std::shared_ptr& pScene); + void setScene(const Scene::SharedPtr& pScene); /** Add a render-pass. The name has to be unique, otherwise the call will be ignored */ - uint32_t addPass(const RenderPass::SharedPtr& pPass, const std::string& passName, PassFlags passFlags = PassFlags::None); + uint32_t addPass(const RenderPass::SharedPtr& pPass, const std::string& passName); /** Get a render-pass */ @@ -76,7 +68,7 @@ namespace Falcor /** Update dictionary for specified render pass. */ - void updatePass(const std::string& passName, const Dictionary& dict); + void updatePass(RenderContext* pRenderContext, const std::string& passName, const Dictionary& dict); /** Insert an edge from a render-pass' output into a different render-pass input. The render passes must be different, the graph must be a DAG. @@ -98,10 +90,6 @@ namespace Falcor */ void removeEdge(uint32_t edgeID); - /** Check if the graph is ready for execution (all passes inputs/outputs have been initialized correctly, no loops in the graph) - */ - bool isValid(std::string& log) const; - /** Execute the graph */ void execute(RenderContext* pContext); @@ -110,10 +98,12 @@ namespace Falcor */ void update(const SharedPtr& pGraph); - /** Set an input resource. The name has the format `renderPassName.resourceName`. - This is an alias for `getRenderPass(renderPassName)->setInput(resourceName, pResource)` + /** Set an external input resource + \param[in] name Input name. Has the format `renderPassName.resourceName` + \param[in] pResource The resource to bind. If this is nullptr, will unregister the resource */ - bool setInput(const std::string& name, const std::shared_ptr& pResource); + void setInput(const std::string& name, const Resource::SharedPtr& pResource); + /** Returns true if a render pass exists by this name in the graph. */ bool doesPassExist(const std::string& name) const { return (mNameToIndex.find(name) != mNameToIndex.end()); } @@ -125,11 +115,17 @@ namespace Falcor /** Get an output resource. The name has the format `renderPassName.resourceName`. This is an alias for `getRenderPass(renderPassName)->getOutput(resourceName)` */ - const std::shared_ptr getOutput(const std::string& name); + Resource::SharedPtr getOutput(const std::string& name); + + /** Get an output resource. The index corresponds to getOutputCount(). + If markOutput() or unmarkOutput() was called, the indices might change so don't cache them + */ + Resource::SharedPtr getOutput(uint32_t index); /** Mark a render-pass output as the graph's output. If the graph has no outputs it is invalid. The name has the format `renderPassName.resourceName`. You can also use `renderPassName` which will allocate all the render-pass outputs. The graph will automatically allocate the output resource + If name is '*', it will mark all available outputs */ void markOutput(const std::string& name); @@ -138,13 +134,17 @@ namespace Falcor */ void unmarkOutput(const std::string& name); + /** Check if a name is marked as output + */ + bool isGraphOutput(const std::string& name); + /** Call this when the swap-chain was resized */ void onResize(const Fbo* pTargetFbo); /** Get the attached scene */ - const std::shared_ptr& getScene() const { return mpScene; } + const Scene::SharedPtr& getScene() const { return mpScene; } /** Get an graph output name from the graph outputs */ @@ -165,7 +165,7 @@ namespace Falcor /** Render the UI */ - void renderUI(Gui* pGui, const char* uiGroup); + void renderUI(Gui::Widgets& widget); /** Enable/disable pass profiling */ @@ -192,19 +192,21 @@ namespace Falcor /** Get the name */ void setName(const std::string& name) { mName = name; } + + /** Compile the graph + */ + bool compile(RenderContext* pContext, std::string& log); + bool compile(RenderContext* pContext) { std::string s; return compile(pContext, s); } + private: friend class RenderGraphUI; friend class RenderGraphExporter; friend class RenderPassLibrary; + friend class RenderGraphCompiler; RenderGraph(const std::string& name); std::string mName; - bool compile(std::string& log); - bool resolveExecutionOrder(); - bool insertAutoPasses(); - bool resolveResourceTypes(); - struct EdgeData { bool autoGenerated = false; @@ -214,22 +216,16 @@ namespace Falcor struct NodeData { - std::string nodeName; + std::string name; RenderPass::SharedPtr pPass; - PassFlags passFlags; }; uint32_t getEdge(const std::string& src, const std::string& dst); void getUnsatisfiedInputs(const NodeData* pNodeData, const RenderPassReflection& passReflection, std::vector& outList) const; void autoConnectPasses(const NodeData* pSrcNode, const RenderPassReflection& srcReflection, const NodeData* pDestNode, std::vector& unsatisfiedInputs); - bool canAutoResolve(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst); - void restoreCompilationChanges(); - - bool mRecompile = true; - std::shared_ptr mpScene; + Scene::SharedPtr mpScene; std::unordered_map mNameToIndex; - DirectedGraph::SharedPtr mpGraph; std::unordered_map mEdgeData; std::unordered_map mNodeData; @@ -250,24 +246,12 @@ namespace Falcor }; std::vector mOutputs; // GRAPH_TODO should this be an unordered set? - bool isGraphOutput(const GraphOut& graphOut) const; - ResourceCache::DefaultProperties mSwapChainData; - - std::vector mExecutionList; - ResourceCache::SharedPtr mpResourcesCache; - - // TODO Better way to track history, or avoid changing the original graph altogether? - struct { - std::vector generatedPasses; - std::vector> removedEdges; - } mCompilationChanges; - bool mProfileGraph = true; Dictionary::SharedPtr mpPassDictionary; + RenderGraphExe::SharedPtr mpExe; + bool mRecompile = false; + RenderGraphCompiler::Dependencies mCompilerDeps; }; - - dlldecl std::vector gRenderGraphs; - enum_class_operators(RenderGraph::PassFlags); } diff --git a/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp b/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp new file mode 100644 index 000000000..ee2bb5cdc --- /dev/null +++ b/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp @@ -0,0 +1,437 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "RenderGraphCompiler.h" +#include "RenderGraph.h" +#include "RenderPasses/ResolvePass.h" + +namespace Falcor +{ + namespace + { + bool canAutoResolve(const RenderPassReflection::Field& src, const RenderPassReflection::Field& dst) + { + return src.getSampleCount() > 1 && dst.getSampleCount() == 1; + } + } + + RenderGraphCompiler::RenderGraphCompiler(RenderGraph& graph, const Dependencies& dependencies) : mGraph(graph), mDependencies(dependencies) {} + + RenderGraphExe::SharedPtr RenderGraphCompiler::compile(RenderGraph& graph, RenderContext* pContext, const Dependencies& dependencies) + { + RenderGraphCompiler c = RenderGraphCompiler(graph, dependencies); + + // Register the external resources + auto pResourcesCache = ResourceCache::create(); + for (const auto&[name, pRes] : dependencies.externalResources) pResourcesCache->registerExternalResource(name, pRes); + + c.resolveExecutionOrder(); + c.compilePasses(pContext); + if (c.insertAutoPasses()) c.resolveExecutionOrder(); + c.validateGraph(); + c.allocateResources(pResourcesCache.get()); + + auto pExe = RenderGraphExe::create(); + pExe->mExecutionList.reserve(c.mExecutionList.size()); + + for (auto e : c.mExecutionList) + { + pExe->insertPass(e.name, e.pPass); + } + c.restoreCompilationChanges(); + pExe->mpResourceCache = pResourcesCache; + return pExe; + } + + void RenderGraphCompiler::validateGraph() const + { + std::string err; + + for (const auto& p : mExecutionList) + { + // Make sure all the inputs are satisfied + for (uint32_t i = 0; i < p.reflector.getFieldCount(); i++) + { + const auto& f = *p.reflector.getField(i); + if (!is_set(f.getVisibility(), RenderPassReflection::Field::Visibility::Input)) continue; + if (is_set(f.getFlags(), RenderPassReflection::Field::Flags::Optional)) continue; + + const DirectedGraph::Node* pGraphNode = mGraph.mpGraph->getNode(p.index); + const std::string& name = f.getName(); + bool found = false; + for (uint32_t e = 0; e < pGraphNode->getIncomingEdgeCount(); e++) + { + const auto& edgeData = mGraph.mEdgeData.at(pGraphNode->getIncomingEdge(e)); + found = (edgeData.dstField == name); + if (found) break; + } + std::string resName = p.name + '.' + name; + bool hasExternal = mDependencies.externalResources.find(resName) != mDependencies.externalResources.end(); + if (hasExternal && found) err += "Input field `" + resName + "` has an incoming edge and an external resource bound. This is illegal"; + if(!hasExternal && !found) err += "Input field `" + resName + "` is required but not satisfied\n"; + } + } + + if (err.size()) throw std::exception(err.c_str()); + } + + void RenderGraphCompiler::resolveExecutionOrder() + { + mExecutionList.clear(); + + // Find out which passes are mandatory + std::unordered_set mandatoryPasses; + for (auto& o : mGraph.mOutputs) mandatoryPasses.insert(o.nodeId); // Add direct-graph outputs + + for (auto& e : mGraph.mEdgeData) // Add all the passes which have an execution-edge connected to them + { + if (e.second.dstField.empty()) + { + assert(e.second.srcField.empty()); + const auto& edge = mGraph.mpGraph->getEdge(e.first); + mandatoryPasses.insert(edge->getDestNode()); + mandatoryPasses.insert(edge->getSourceNode()); + } + } + + // Find all passes that affect the outputs + std::unordered_set participatingPasses; + for (auto& o : mandatoryPasses) + { + uint32_t nodeId = o; + auto dfs = DirectedGraphDfsTraversal(mGraph.mpGraph, nodeId, DirectedGraphDfsTraversal::Flags::IgnoreVisited | DirectedGraphDfsTraversal::Flags::Reverse); + while (nodeId != DirectedGraph::kInvalidID) + { + participatingPasses.insert(nodeId); + nodeId = dfs.traverse(); + } + } + + // Run topological sort + auto topologicalSort = DirectedGraphTopologicalSort::sort(mGraph.mpGraph.get()); + + // For each object in the vector, if it's being used in the execution, put it in the list + for (auto& node : topologicalSort) + { + if (participatingPasses.find(node) != participatingPasses.end()) + { + const auto pData = mGraph.mNodeData[node]; + mExecutionList.push_back({ node, pData.pPass, pData.name, pData.pPass->reflect({}) }); + } + } + } + + bool RenderGraphCompiler::insertAutoPasses() + { + bool addedPasses = false; + for (size_t i = 0; i < mExecutionList.size(); i++) + { + const RenderPassReflection& passReflection = mExecutionList[i].reflector; + + // Check for opportunities to automatically resolve MSAA + // - Only take explicitly specified MS output + for (uint32_t f = 0; f < passReflection.getFieldCount(); f++) + { + // Iterate over output fields + auto& srcField = *passReflection.getField(f); + if (is_set(srcField.getVisibility(), RenderPassReflection::Field::Visibility::Output) == false) continue; + + const std::string& srcPassName = mExecutionList[i].name; + // Gather src field name, and every input it is connected to + std::string srcFieldName = srcPassName + '.' + srcField.getName(); + std::vector dstFieldNames; + + const DirectedGraph::Node* pNode = mGraph.mpGraph->getNode(mExecutionList[i].index); + for (uint32_t e = 0; e < pNode->getOutgoingEdgeCount(); e++) + { + uint32_t edgeIndex = pNode->getOutgoingEdge(e); + const auto& edgeData = mGraph.mEdgeData.at(edgeIndex); + + // For every output field, iterate over all edges extending from that field + if (srcField.getName() == edgeData.srcField) + { + const auto& pEdge = mGraph.mpGraph->getEdge(edgeIndex); + const std::string& dstPassName = mGraph.mNodeData.at(pEdge->getDestNode()).name; + + // If edge is connected to something that isn't executed, ignore + auto getPassReflection = [&](uint32_t index) -> std::optional + { + for (const auto& e : mExecutionList) if (e.index == index) return e.reflector; + return std::nullopt; + }; + + const auto& dstReflection = getPassReflection(pEdge->getDestNode()); + if (!dstReflection) continue; + const auto& dstField = *dstReflection->getField(edgeData.dstField); + + assert(srcField.isValid() && dstField.isValid()); + if (canAutoResolve(srcField, dstField)) + { + std::string dstFieldName = dstPassName + '.' + dstField.getName(); + dstFieldNames.push_back(dstFieldName); + } + } + } + + // If there are connections to add MSAA Resolve + if (dstFieldNames.size() > 0) + { + // One resolve pass is made for every output that requires it + auto pResolvePass = ResolvePass::create(); + pResolvePass->setFormat(srcField.getFormat()); // Match input texture format + + // Create pass and attach src to it + std::string resolvePassName = srcFieldName + "-ResolvePass"; + mGraph.addPass(pResolvePass, resolvePassName); + mGraph.addEdge(srcFieldName, resolvePassName + ".src"); + + // For every input the src field is connected to, connect the resolve pass output to the input + for (const auto& dstFieldName : dstFieldNames) + { + // Remove original edge + mGraph.removeEdge(srcFieldName, dstFieldName); + // Replace with edge coming from resolve output + mGraph.addEdge(resolvePassName + ".dst", dstFieldName); + + // Log changes made to user's graph by compilation process + mCompilationChanges.removedEdges.emplace_back(srcFieldName, dstFieldName); + } + + mCompilationChanges.generatedPasses.push_back(resolvePassName); + addedPasses = true; + } + } + } + + return addedPasses; + } + + void RenderGraphCompiler::allocateResources(ResourceCache* pResourceCache) + { + // Build list to look up execution order index from the pass + std::unordered_map passToIndex; + for (size_t i = 0; i < mExecutionList.size(); i++) + { + passToIndex.emplace(mExecutionList[i].pPass.get(), uint32_t(i)); + } + + for (size_t i = 0; i < mExecutionList.size(); i++) + { + uint32_t nodeIndex = mExecutionList[i].index; + + const DirectedGraph::Node* pNode = mGraph.mpGraph->getNode(nodeIndex); + assert(pNode); + RenderPass* pCurrPass = mGraph.mNodeData[nodeIndex].pPass.get(); + const auto& passReflection = mExecutionList[i].reflector; + + auto isResourceUsed = [&](auto field) + { + if (!is_set(field.getFlags(), RenderPassReflection::Field::Flags::Optional)) return true; + if(mGraph.isGraphOutput({ nodeIndex, field.getName() })) return true; + for (uint32_t e = 0; e < pNode->getOutgoingEdgeCount(); e++) + { + const auto& edgeData = mGraph.mEdgeData[pNode->getOutgoingEdge(e)]; + if (edgeData.srcField == field.getName()) return true; + } + return false; + }; + + // Register all pass outputs + for (size_t f = 0; f < passReflection.getFieldCount(); f++) + { + auto field = *passReflection.getField(f); + std::string fullFieldName = mGraph.mNodeData[nodeIndex].name + '.' + field.getName(); + + // Skip input resources, we never allocate them + if (!is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input)) + { + if (isResourceUsed(field) == false) continue; + + // Resource lifetime for graph outputs must extend to end of graph execution + bool graphOutput = mGraph.isGraphOutput({ nodeIndex, field.getName() }); + uint32_t lifetime = graphOutput ? uint32_t(-1) : uint32_t(i); + if (graphOutput && field.getBindFlags() != ResourceBindFlags::None) field.bindFlags(field.getBindFlags() | ResourceBindFlags::ShaderResource); // Adding ShaderResource for graph outputs + pResourceCache->registerField(fullFieldName, field, lifetime); + } + } + + // Go over the pass inputs, add them as aliases to the outputs that connect to them (which should be already registered above) + for (uint32_t e = 0; e < pNode->getIncomingEdgeCount(); e++) + { + uint32_t edgeIndex = pNode->getIncomingEdge(e); + const auto& pEdge = mGraph.mpGraph->getEdge(edgeIndex); + const auto& edgeData = mGraph.mEdgeData[edgeIndex]; + + // Skip execution-edges + if (edgeData.dstField.empty()) + { + assert(edgeData.srcField.empty()); + continue; + } + + const auto& dstField = *passReflection.getField(edgeData.dstField); + assert(dstField.isValid() && is_set(dstField.getVisibility(), RenderPassReflection::Field::Visibility::Input)); + + // Merge dst/input field into same resource data + std::string srcFieldName = mGraph.mNodeData[pEdge->getSourceNode()].name + '.' + edgeData.srcField; + std::string dstFieldName = mGraph.mNodeData[nodeIndex].name + '.' + dstField.getName(); + + const auto& pSrcPass = mGraph.mNodeData[pEdge->getSourceNode()].pPass.get(); + const auto& srcReflection = mExecutionList[passToIndex.at(pSrcPass)].reflector; + pResourceCache->registerField(dstFieldName, dstField, passToIndex[pSrcPass], srcFieldName); + } + } + + pResourceCache->allocateResources(mDependencies.defaultResourceProps); + } + + + void RenderGraphCompiler::restoreCompilationChanges() + { + for (const auto& name : mCompilationChanges.generatedPasses) mGraph.removePass(name); + for (const auto& e : mCompilationChanges.removedEdges) mGraph.addEdge(e.first, e.second); + + mCompilationChanges.generatedPasses.clear(); + mCompilationChanges.removedEdges.clear(); + } + + RenderPass::CompileData RenderGraphCompiler::prepPassCompilationData(const PassData& passData) + { + RenderPass::CompileData compileData; + compileData.defaultTexDims = mDependencies.defaultResourceProps.dims; + compileData.defaultTexFormat = mDependencies.defaultResourceProps.format; + + auto isExecutionEdge = [this](uint32_t edgeId) + { + return mGraph.mEdgeData[edgeId].srcField.empty(); + }; + + // Get the list of input resources + const auto pNode = mGraph.mpGraph->getNode(passData.index); + for (uint32_t i = 0; i < pNode->getIncomingEdgeCount(); i++) + { + uint32_t e = pNode->getIncomingEdge(i); + if (isExecutionEdge(e)) continue; + + uint32_t incomingPass = mGraph.mpGraph->getEdge(e)->getSourceNode(); + for (const auto& otherPass : mExecutionList) + { + if (otherPass.index == incomingPass) + { + auto f = *otherPass.reflector.getField(mGraph.mEdgeData[e].srcField); + const auto& fIn = *passData.reflector.getField(mGraph.mEdgeData[e].dstField); + f.name(fIn.getName()).visibility(fIn.getVisibility()).desc(fIn.getDesc()); + compileData.connectedResources.addField(f); + break; + } + else if (otherPass.index == passData.index) break; + } + } + + // Add the external resources + for (auto& [name, pRes] : mDependencies.externalResources) + { + if (hasPrefix(name, passData.name + ".")) + { + auto pTex = pRes->asTexture(); + std::string resName = name.substr((passData.name + ".").size()); + compileData.connectedResources.addInput(resName, "External input resource").format(pTex->getFormat()).resourceType(resourceTypeToFieldType(pTex->getType()), pTex->getWidth(), pTex->getHeight(), pTex->getDepth(), pTex->getSampleCount(), pTex->getMipCount(), pTex->getArraySize()); + } + } + + // Get a list of output resources. It's slightly different then the inputs, because we can have multiple edges for each output resource + for (uint32_t i = 0; i < pNode->getOutgoingEdgeCount(); i++) + { + uint32_t e = pNode->getOutgoingEdge(i); + if (isExecutionEdge(e)) continue; + + uint32_t outgoingPass = mGraph.mpGraph->getEdge(e)->getDestNode(); + for (const auto& otherPass : mExecutionList) + { + if (otherPass.index == outgoingPass) + { + auto f = *otherPass.reflector.getField(mGraph.mEdgeData[e].dstField); + auto pField = compileData.connectedResources.getField(mGraph.mEdgeData[e].srcField); + if (pField) + { + const_cast(pField)->merge(f); + } + else + { + const auto& fOut = *passData.reflector.getField(mGraph.mEdgeData[e].srcField); + f.name(fOut.getName()).visibility(fOut.getVisibility()).desc(fOut.getDesc()); + compileData.connectedResources.addField(f); + } + } + } + } + + return compileData; + } + + void RenderGraphCompiler::compilePasses(RenderContext* pContext) + { + while(1) + { + std::string log; + bool success = true; + for (auto& p : mExecutionList) + { + try + { + p.pPass->compile(pContext, prepPassCompilationData(p)); + } + catch (std::exception e) + { + log += std::string(e.what()) + "\n"; + success = false; + } + } + + if (success) return; + + // Retry + bool changed = false; + for (auto& p : mExecutionList) + { + auto newR = p.pPass->reflect(prepPassCompilationData(p)); + if (newR != p.reflector) + { + p.reflector = newR; + changed = true; + } + } + + if (!changed) + { + logError("Graph compilation failed.\n" + log); + return; + } + } + } +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphScripting.h b/Source/Falcor/RenderGraph/RenderGraphCompiler.h similarity index 55% rename from Framework/Source/Experimental/RenderGraph/RenderGraphScripting.h rename to Source/Falcor/RenderGraph/RenderGraphCompiler.h index f58ce67a2..fff30992d 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphScripting.h +++ b/Source/Falcor/RenderGraph/RenderGraphCompiler.h @@ -26,51 +26,50 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderGraph.h" - -namespace pybind11 -{ - class module; -}; +#include "ResourceCache.h" +#include "RenderGraphExe.h" namespace Falcor { - class RenderGraphScripting + class RenderGraph; + + class dlldecl RenderGraphCompiler { public: - using SharedPtr = std::shared_ptr; - using GraphDesc = Scripting::Context::ObjectDesc; + struct Dependencies + { + ResourceCache::DefaultProperties defaultResourceProps; + ResourceCache::ResourcesMap externalResources; + }; + static RenderGraphExe::SharedPtr compile(RenderGraph& graph, RenderContext* pContext, const Dependencies& dependencies); - using GraphVec = std::vector; - - static SharedPtr create(); - static SharedPtr create(const std::string& filename); + private: + RenderGraphCompiler(RenderGraph& graph, const Dependencies& dependencies); + RenderGraph& mGraph; + const Dependencies& mDependencies; - // Python to C++ - static void registerScriptingObjects(pybind11::module& m); - bool runScript(const std::string& script); - const GraphVec& getGraphs() const { return mGraphVec; } - RenderGraph::SharedPtr getGraph(const std::string& name) const; - void addGraph(const std::string& name, const RenderGraph::SharedPtr& pGraph); + struct PassData + { + uint32_t index; + RenderPass::SharedPtr pPass; + std::string name; + RenderPassReflection reflector; + }; + std::vector mExecutionList; - static const char* kAddPass; - static const char* kRemovePass; - static const char* kAddEdge; - static const char* kRemoveEdge; - static const char* kMarkOutput; - static const char* kUnmarkOutput; - static const char* kAutoGenEdges; - static const char* kCreatePass; - static const char* kCreateGraph; - static const char* kUpdatePass; - static const char* kSetName; - static const char* kSetScene; - static const char* kLoadPassLibrary; + // TODO Better way to track history, or avoid changing the original graph altogether? + struct + { + std::vector generatedPasses; + std::vector> removedEdges; + } mCompilationChanges; - private: - RenderGraphScripting() = default; - Scripting::Context mContext; - GraphVec mGraphVec; - std::string mFilename; + void resolveExecutionOrder(); + void compilePasses(RenderContext* pContext); + bool insertAutoPasses(); + void allocateResources(ResourceCache* pResourceCache); + void validateGraph() const; + void restoreCompilationChanges(); + RenderPass::CompileData prepPassCompilationData(const PassData& passData); }; } diff --git a/Source/Falcor/RenderGraph/RenderGraphExe.cpp b/Source/Falcor/RenderGraph/RenderGraphExe.cpp new file mode 100644 index 000000000..f5a5d5be9 --- /dev/null +++ b/Source/Falcor/RenderGraph/RenderGraphExe.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "RenderGraphExe.h" + +namespace Falcor +{ + void RenderGraphExe::execute(const Context& ctx) + { + bool profile = ctx.profileGraph && gProfileEnabled; + + if (profile) Profiler::startEvent("RenderGraphExe::execute()"); + + for (const auto& pass : mExecutionList) + { + if (profile) Profiler::startEvent(pass.name); + RenderData renderData(pass.name, mpResourceCache, ctx.pGraphDictionary, ctx.defaultTexDims, ctx.defaultTexFormat); + pass.pPass->execute(ctx.pRenderContext, renderData); + + if (profile) Profiler::endEvent(pass.name); + } + + if (profile) Profiler::endEvent("RenderGraphExe::execute()"); + } + + void RenderGraphExe::renderUI(Gui::Widgets& widget) + { + for (const auto& p : mExecutionList) + { + const auto& pPass = p.pPass; + + auto passGroup = Gui::Group(widget, p.name); + if (passGroup.open()) + { + // If you are thinking about displaying the profiler results next to the group label, it won't work. Since the times change every frame, IMGUI thinks it's a different group and will not expand it + const auto& desc = pPass->getDesc(); + if (desc.size()) passGroup.tooltip(desc.c_str()); + pPass->renderUI(passGroup); + + passGroup.release(); + } + } + } + + bool RenderGraphExe::onMouseEvent(const MouseEvent& mouseEvent) + { + bool b = false; + for (const auto& p : mExecutionList) + { + const auto& pPass = p.pPass; + b = b || pPass->onMouseEvent(mouseEvent); + } + return b; + } + + bool RenderGraphExe::onKeyEvent(const KeyboardEvent& keyEvent) + { + bool b = false; + for (const auto& p : mExecutionList) + { + const auto& pPass = p.pPass; + b = b || pPass->onKeyEvent(keyEvent); + } + return b; + } + + void RenderGraphExe::insertPass(const std::string& name, const RenderPass::SharedPtr& pPass) + { + mExecutionList.push_back(Pass(name, pPass)); + } + + Resource::SharedPtr RenderGraphExe::getResource(const std::string& name) const + { + assert(mpResourceCache); + return mpResourceCache->getResource(name); + } + + void RenderGraphExe::setInput(const std::string& name, const Resource::SharedPtr& pResource) + { + mpResourceCache->registerExternalResource(name, pResource); + } +} diff --git a/Source/Falcor/RenderGraph/RenderGraphExe.h b/Source/Falcor/RenderGraph/RenderGraphExe.h new file mode 100644 index 000000000..35a1dce0e --- /dev/null +++ b/Source/Falcor/RenderGraph/RenderGraphExe.h @@ -0,0 +1,97 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "ResourceCache.h" +#include "Utils/Scripting/Dictionary.h" +#include "RenderPass.h" + +namespace Falcor +{ + class RenderGraphCompiler; + + class dlldecl RenderGraphExe + { + public: + using SharedPtr = std::shared_ptr; + struct Context + { + RenderContext* pRenderContext; + Dictionary::SharedPtr pGraphDictionary; + bool profileGraph; + uvec2 defaultTexDims; + ResourceFormat defaultTexFormat; + }; + + /** Execute the graph + */ + void execute(const Context& ctx); + + /** Render the UI + */ + void renderUI(Gui::Widgets& widget); + + /** Mouse event handler. + Returns true if the event was handled by the object, false otherwise + */ + bool onMouseEvent(const MouseEvent& mouseEvent); + + /** Keyboard event handler + Returns true if the event was handled by the object, false otherwise + */ + bool onKeyEvent(const KeyboardEvent& keyEvent); + + /** Get a resource from the cache + */ + Resource::SharedPtr getResource(const std::string& name) const; + + /** Set an external input resource + \param[in] name Input name. Has the format `renderPassName.resourceName` + \param[in] pResource The resource to bind. If this is nullptr, will unregister the resource + */ + void setInput(const std::string& name, const Resource::SharedPtr& pResource); + + private: + friend class RenderGraphCompiler; + static SharedPtr create() { return SharedPtr(new RenderGraphExe); } + RenderGraphExe() = default; + + void insertPass(const std::string& name, const RenderPass::SharedPtr& pPass); + + struct Pass + { + std::string name; + RenderPass::SharedPtr pPass; + private: + friend class RenderGraphExe; // Force RenderGraphCompiler to use insertPass() by hiding this Ctor from it + Pass(const std::string& name_, const RenderPass::SharedPtr& pPass_) : name(name_), pPass(pPass_) {} + }; + + std::vector mExecutionList; + ResourceCache::SharedPtr mpResourceCache; + }; +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphIR.cpp b/Source/Falcor/RenderGraph/RenderGraphIR.cpp similarity index 65% rename from Framework/Source/Experimental/RenderGraph/RenderGraphIR.cpp rename to Source/Falcor/RenderGraph/RenderGraphIR.cpp index 558887c48..fa7e6b1dd 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphIR.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphIR.cpp @@ -25,17 +25,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RenderGraphIR.h" #include "RenderGraph.h" -#include "RenderGraphScripting.h" -#include "Utils/Scripting/ScriptBindings.h" -#include "Utils/Dictionary.h" #include "Utils/StringUtils.h" -#include "Graphics/Scene/Scene.h" namespace Falcor { + const char* RenderGraphIR::kAddPass = "addPass"; + const char* RenderGraphIR::kRemovePass = "removePass"; + const char* RenderGraphIR::kAddEdge = "addEdge"; + const char* RenderGraphIR::kRemoveEdge = "removeEdge"; + const char* RenderGraphIR::kMarkOutput = "markOutput"; + const char* RenderGraphIR::kUnmarkOutput = "unmarkOutput"; + const char* RenderGraphIR::kAutoGenEdges = "autoGenEdges"; + const char* RenderGraphIR::kUpdatePass = "updatePass"; + const char* RenderGraphIR::kLoadPassLibrary = "loadRenderPassLibrary"; + const char* RenderGraphIR::kRenderPass = "RenderPass"; + const char* RenderGraphIR::kRenderGraph = "RenderGraph"; + std::string addQuotes(const std::string& s) { return '"' + s + '"'; @@ -69,16 +77,21 @@ namespace Falcor return call; } + std::string RenderGraphIR::getFuncName(const std::string& graphName) + { + return "render_graph_" + graphName; + } + RenderGraphIR::RenderGraphIR(const std::string& name, bool newGraph) : mName(name) { if(newGraph) { - mIR += "def render_graph_" + mName + "():\n"; - mIndentation = "\t"; + mIR += "def " + getFuncName(mName) + "():\n"; + mIndentation = " "; mGraphPrefix += mIndentation; - mIR += mIndentation + mName + " = " + funcCall(RenderGraphScripting::kCreateGraph); + mIR += mIndentation + "g" + " = " + funcCall(kRenderGraph, addQuotes(mName)); } - mGraphPrefix += mName + '.'; + mGraphPrefix += "g."; }; RenderGraphIR::SharedPtr RenderGraphIR::create(const std::string& name, bool newGraph) @@ -92,63 +105,52 @@ namespace Falcor if(dictionary.size()) { std::string dictionaryStr = dictionary.toString(); - mIR += funcCall(RenderGraphScripting::kCreatePass, addQuotes(passClass), dictionaryStr); + mIR += funcCall(RenderGraphIR::kRenderPass, addQuotes(passClass), dictionaryStr); } else { - mIR += funcCall(RenderGraphScripting::kCreatePass, addQuotes(passClass)); + mIR += funcCall(RenderGraphIR::kRenderPass, addQuotes(passClass)); } - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kAddPass, passName, addQuotes(passName)); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kAddPass, passName, addQuotes(passName)); } void RenderGraphIR::updatePass(const std::string& passName, const Dictionary& dictionary) { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kUpdatePass, addQuotes(passName), dictionary.toString()); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kUpdatePass, addQuotes(passName), dictionary.toString()); } void RenderGraphIR::removePass(const std::string& passName) { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kRemovePass, addQuotes(passName)); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kRemovePass, addQuotes(passName)); } void RenderGraphIR::addEdge(const std::string& src, const std::string& dst) { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kAddEdge, addQuotes(src), addQuotes(dst)); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kAddEdge, addQuotes(src), addQuotes(dst)); } void RenderGraphIR::removeEdge(const std::string& src, const std::string& dst) { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kRemoveEdge, addQuotes(src), addQuotes(dst)); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kRemoveEdge, addQuotes(src), addQuotes(dst)); } void RenderGraphIR::markOutput(const std::string& name) { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kMarkOutput, addQuotes(name)); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kMarkOutput, addQuotes(name)); } void RenderGraphIR::unmarkOutput(const std::string& name) { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kUnmarkOutput, addQuotes(name)); - } - - void RenderGraphIR::setScene(const Scene* pScene) - { - const auto& sceneFile = pScene->getFilename(); - std::string scenePath = replaceSubstring(sceneFile, "\\", "/"); - std::string sceneName = getFilenameFromPath(scenePath); - auto extPos = sceneName.find_last_of('.'); - if (extPos != std::string::npos) sceneName = sceneName.substr(0, extPos); - mIR += mIndentation + sceneName + " = " + funcCall(ScriptBindings::kLoadScene, addQuotes(scenePath)); - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kSetScene, sceneName); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kUnmarkOutput, addQuotes(name)); } void RenderGraphIR::loadPassLibrary(const std::string& name) { - mIR += mIndentation + funcCall(RenderGraphScripting::kLoadPassLibrary, addQuotes(name)); + mIR += mIndentation + funcCall(RenderGraphIR::kLoadPassLibrary, addQuotes(name)); } void RenderGraphIR::autoGenEdges() { - mIR += mGraphPrefix + funcCall(RenderGraphScripting::kAutoGenEdges); + mIR += mGraphPrefix + funcCall(RenderGraphIR::kAutoGenEdges); } -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphIR.h b/Source/Falcor/RenderGraph/RenderGraphIR.h similarity index 81% rename from Framework/Source/Experimental/RenderGraph/RenderGraphIR.h rename to Source/Falcor/RenderGraph/RenderGraphIR.h index 48ba6766b..3c826413f 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphIR.h +++ b/Source/Falcor/RenderGraph/RenderGraphIR.h @@ -26,19 +26,18 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Utils/Dictionary.h" +#include "Utils/Scripting/Dictionary.h" namespace Falcor { - class RenderGraph; - class Dictionary; class Scene; - class RenderGraphIR + class dlldecl RenderGraphIR { public: using SharedPtr = std::shared_ptr; + static std::string getFuncName(const std::string& graphName); static SharedPtr create(const std::string& name, bool newGraph = true); void addPass(const std::string& passClass, const std::string& passName, const Dictionary& = Dictionary()); @@ -49,11 +48,21 @@ namespace Falcor void markOutput(const std::string& name); void unmarkOutput(const std::string& name); void loadPassLibrary(const std::string& name); - void setScene(const Scene* pScene); void autoGenEdges(); - std::string getIR() { return mIR + mIndentation + (mIndentation.size() ? ("return " + mName + '\n') : "\n"); } + std::string getIR() { return mIR + mIndentation + (mIndentation.size() ? "return g\n" : "\n"); } + static const char* kAddPass; + static const char* kRemovePass; + static const char* kAddEdge; + static const char* kRemoveEdge; + static const char* kMarkOutput; + static const char* kUnmarkOutput; + static const char* kAutoGenEdges; + static const char* kRenderPass; + static const char* kRenderGraph; + static const char* kUpdatePass; + static const char* kLoadPassLibrary; private: RenderGraphIR(const std::string& name, bool newGraph); std::string mName; @@ -61,4 +70,4 @@ namespace Falcor std::string mIndentation; std::string mGraphPrefix; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp new file mode 100644 index 000000000..dac408f5c --- /dev/null +++ b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp @@ -0,0 +1,176 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "RenderGraphImportExport.h" +#include "RenderPassLibrary.h" +#include "RenderGraphIR.h" +#include + +namespace Falcor +{ + namespace + { + void updateGraphStrings(std::string& graph, std::string& file, std::string& func) + { + graph = graph.empty() ? "renderGraph" : graph; + file = file.empty() ? graph + ".py" : file; + func = func.empty() ? RenderGraphIR::getFuncName(graph) : func; + } + + void runScriptFile(const std::string& filename, const std::string& custom) + { + std::string fullpath; + if (findFileInDataDirectories(filename, fullpath) == false) + { + throw std::exception("Can't find the file"); + } + + std::string script = readFile(fullpath) + custom; + Scripting::runScript(script); + } + } + + bool loadFailed(std::exception e, const std::string& filename) + { + logError(e.what(), Logger::MsgBox::Nope); + auto res = msgBox(std::string("Error when importing graph from file `" + filename + "`\n" + e.what() + "\n\nWould you like to try and reload the file?").c_str(), MsgBoxType::YesNo); + return (res == MsgBoxButton::No); + } + + RenderGraph::SharedPtr RenderGraphImporter::import(std::string graphName, std::string filename, std::string funcName) + { + while(true) + { + try + { + updateGraphStrings(graphName, filename, funcName); + std::string custom; + if (funcName.size()) custom += "\n" + graphName + '=' + funcName + "()"; + runScriptFile(filename, custom); + + auto pGraph = Scripting::getGlobalContext().getObject(graphName); + if (!pGraph) throw("Unspecified error"); + + pGraph->setName(graphName); + return pGraph; + } + catch (std::exception e) + { + if (loadFailed(e, filename)) return nullptr; + } + } + } + + std::vector RenderGraphImporter::importAllGraphs(const std::string& filename) + { + while(true) + { + try + { + runScriptFile(filename, {}); + auto scriptObj = Scripting::getGlobalContext().getObjects(); + std::vector res; + res.reserve(scriptObj.size()); + + for (const auto& s : scriptObj) + { + s.obj->setName(s.name); + res.push_back(s.obj); + } + + return res; + } + catch (std::exception e) + { + if (loadFailed(e, filename)) return {}; + } + } + } + + std::string RenderGraphExporter::getFuncName(const std::string& graphName) + { + return RenderGraphIR::getFuncName(graphName); + } + + std::string RenderGraphExporter::getIR(const RenderGraph::SharedPtr& pGraph) + { + RenderGraphIR::SharedPtr pIR = RenderGraphIR::create(pGraph->getName()); + + // Register passes that are loaded from dlls + auto libNames = RenderPassLibrary::enumerateLibraries(); + for (const auto& libName : libNames) + { + pIR->loadPassLibrary(getFilenameFromPath(libName)); + } + + // Add the passes + for (const auto& node : pGraph->mNodeData) + { + const auto& data = node.second; + pIR->addPass(getClassTypeName(data.pPass.get()), data.name, data.pPass->getScriptingDictionary()); + } + + // Add the edges + for (const auto& edge : pGraph->mEdgeData) + { + const auto& data = edge.second; + const auto& srcPass = pGraph->mNodeData[pGraph->mpGraph->getEdge(edge.first)->getSourceNode()].name; + const auto& dstPass = pGraph->mNodeData[pGraph->mpGraph->getEdge(edge.first)->getDestNode()].name; + std::string src = srcPass + (data.srcField.size() ? '.' + data.srcField : data.srcField); + std::string dst = dstPass + (data.dstField.size() ? '.' + data.dstField : data.dstField); + pIR->addEdge(src, dst); + } + + // Graph outputs + for (const auto& out : pGraph->mOutputs) + { + std::string str = pGraph->mNodeData[out.nodeId].name + '.' + out.field; + pIR->markOutput(str); + } + + return pIR->getIR(); + } + + bool RenderGraphExporter::save(const std::shared_ptr& pGraph, std::string filename) + { + std::string ir = getIR(pGraph); + std::string funcName; + std::string graphName = pGraph->getName(); + updateGraphStrings(graphName, filename, funcName); + + // Save it to file + std::ofstream f(filename); + f << ir << std::endl; + f << graphName << " = " << funcName + "()\n"; + // Try adding it to Mogwai + f << "try: m.addGraph(" + graphName + ")\n"; + f << "except NameError: None\n"; + + return true; + } +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.h b/Source/Falcor/RenderGraph/RenderGraphImportExport.h similarity index 75% rename from Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.h rename to Source/Falcor/RenderGraph/RenderGraphImportExport.h index a7870dd1b..007bb6370 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphImportExport.h +++ b/Source/Falcor/RenderGraph/RenderGraphImportExport.h @@ -26,13 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once +#include "RenderGraph.h" namespace Falcor { - class RenderGraph; - class Fbo; - - class RenderGraphImporter + class dlldecl RenderGraphImporter { public: /** Import a graph from a file. @@ -41,28 +39,18 @@ namespace Falcor \param[in] funcName The function name inside the graph script. If the string is empty, will try invoking a function called `render_graph_()` \return A new render-graph object or nullptr if something went horribly wrong */ - static std::shared_ptr import(std::string graphName, std::string filename = {}, std::string funcName = {}, const Fbo* pDstFbo = nullptr); - - struct GraphData - { - std::string name; - std::shared_ptr pGraph; - }; + static RenderGraph::SharedPtr import(std::string graphName, std::string filename = {}, std::string funcName = {}); /** Import all the graphs found in the script's global namespace */ - static std::vector importAllGraphs(const std::string& filename, const Fbo* pDstFbo = nullptr); + static std::vector importAllGraphs(const std::string& filename); }; - class RenderGraphExporter + class dlldecl RenderGraphExporter { public: - enum class ExportFlags - { - None, - SetScene - }; - - static bool save(const std::shared_ptr& pGraph, std::string graphName, std::string filename = {}, std::string funcName = {}, ExportFlags exportFlags = ExportFlags::None); + static std::string getIR(const RenderGraph::SharedPtr& pGraph); + static std::string getFuncName(const std::string& graphName); + static bool save(const RenderGraph::SharedPtr& pGraph, std::string filename = {}); }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphUI.cpp b/Source/Falcor/RenderGraph/RenderGraphUI.cpp similarity index 84% rename from Framework/Source/Experimental/RenderGraph/RenderGraphUI.cpp rename to Source/Falcor/RenderGraph/RenderGraphUI.cpp index 4fb2ad5e5..3270dc21f 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphUI.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphUI.cpp @@ -25,16 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RenderGraphUI.h" +#include "dear_imgui/imgui.h" +#include "dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h" +#include "dear_imgui/imgui_internal.h" +#include #include "RenderPassLibrary.h" -#include "Utils/Gui.h" -#include "Externals/dear_imgui/imgui.h" -#include "Externals/dear_imgui_addons/imguinodegrapheditor/imguinodegrapheditor.h" -#include "Externals/dear_imgui/imgui.h" -#include "Externals/dear_imgui/imgui_internal.h" -#include -#include namespace Falcor { @@ -231,14 +228,14 @@ namespace Falcor bool isInputs = true; std::string dummyText; dummyText.resize(32, ' '); - pGui->addText(dummyText.c_str()); - pGui->addText(dummyText.c_str()); - pGui->addText(dummyText.c_str()); - pGui->addText(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); for (int32_t i = 0; i < paddingSpace; ++i) { - pGui->addText(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); } for (uint32_t j = 0; j < 2; ++j) @@ -290,7 +287,7 @@ namespace Falcor ImGui::SetCursorScreenPos({ inputPos.x + pinOffsetx - ((pinOffsetx < 0.0f) ? ImGui::CalcTextSize(isInputs ? pCurrentNode->InputNames[i] : pCurrentNode->OutputNames[i]).x : 0.0f), inputPos.y - kPinRadius }); slotNum++; - if (drawLabel) pGui->addText(pText); + if (drawLabel) ImGui::TextUnformatted(pText); } // reset and set up offsets for the output pins @@ -306,10 +303,10 @@ namespace Falcor for (int32_t i = 0; i < paddingSpace; ++i) { - pGui->addText(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); } - pGui->addText(dummyText.c_str()); + ImGui::TextUnformatted(dummyText.c_str()); return false; } @@ -324,7 +321,6 @@ namespace Falcor mpRenderPass = pRenderPass; const glm::vec4 nodeColor = Gui::pickUniqueColor(pRenderPass->getName()); overrideTitleBgColor = ImGui::GetColorU32({ nodeColor.x, nodeColor.y, nodeColor.z, nodeColor.w }); - } bool isInputs = true; @@ -508,10 +504,10 @@ namespace Falcor if (!(dstField[0] == '#')) { - RenderPassReflection srcReflection = mpRenderGraph->mNodeData[mpRenderGraph->getPassIndex(srcPass)].pPass->reflect(); - RenderPassReflection dstReflection = mpRenderGraph->mNodeData[mpRenderGraph->getPassIndex(dstPass)].pPass->reflect(); + RenderPassReflection srcReflection = mpRenderGraph->mNodeData[mpRenderGraph->getPassIndex(srcPass)].pPass->reflect({}); + RenderPassReflection dstReflection = mpRenderGraph->mNodeData[mpRenderGraph->getPassIndex(dstPass)].pPass->reflect({}); - bool canAutoResolve = mpRenderGraph->canAutoResolve(srcReflection.getField(srcField), dstReflection.getField(dstField)); + bool canAutoResolve = false;// mpRenderGraph->canAutoResolve(srcReflection.getField(srcField), dstReflection.getField(dstField)); if (canAutoResolve && mDisplayAutoResolvePopup) canCreateEdge = autoResolveWarning(srcString, dstString); color = canAutoResolve ? kAutoResolveEdgesColor : kEdgesColor; } @@ -563,46 +559,35 @@ namespace Falcor mShouldUpdate = true; } - void RenderGraphUI::updateGraph() + void RenderGraphUI::updateGraph(RenderContext* pContext) { if (!mShouldUpdate) return; std::string newCommands = mpIr->getIR(); mpIr = RenderGraphIR::create(mRenderGraphName, false); // reset if (mLastCommand == newCommands) return; - // make sure the graph is compiled - mpRenderGraph->resolveExecutionOrder(); mLastCommand = newCommands; - if (mRecordUpdates) mUpdateCommands += newCommands; - // update reference graph to check if valid before sending to next - auto pScripting = RenderGraphScripting::create(); - pScripting->addGraph(mRenderGraphName, mpRenderGraph); - pScripting->runScript(newCommands); - - if(newCommands.size()) mLogString += "Running: " + newCommands; + // update reference graph to check if valid before sending to next + Scripting::getGlobalContext().setObject("g", mpRenderGraph); + Scripting::runScript(newCommands); + if(newCommands.size()) mLogString += newCommands; // only send updates that we know are valid. - if (!mpRenderGraph->isValid(mLogString)) - { - mLogString += "Graph is currently invalid\n"; - } - + if (mpRenderGraph->compile(pContext) == false) mLogString += "Graph is currently invalid\n"; mShouldUpdate = false; mRebuildDisplayData = true; } - void RenderGraphUI::writeUpdateScriptToFile(const std::string& filePath, float lastFrameTime) + void RenderGraphUI::writeUpdateScriptToFile(RenderContext* pContext, const std::string& filePath, float lastFrameTime) { if ((mTimeSinceLastUpdate += lastFrameTime) < kUpdateTimeInterval) return; mTimeSinceLastUpdate = 0.0f; if (!mUpdateCommands.size()) return; // only send delta of updates once the graph is valid - std::string log; - if (!mpRenderGraph->isValid(log)) return; - + if (mpRenderGraph->compile(pContext) == false) return; std::ofstream outputFileStream(filePath, std::ios_base::out); outputFileStream << mUpdateCommands; mUpdateCommands.clear(); @@ -640,7 +625,7 @@ namespace Falcor } if (state == ImGui::NodeGraphEditor::NodeState::NS_ADDED) { - pGraphEditor->getRenderGraphUI()->addRenderPass(node->getName(), pRenderGraphNode->mpRenderPass->getName()); + pGraphEditor->getRenderGraphUI()->addRenderPass(node->getName(), getClassTypeName(pRenderGraphNode->mpRenderPass)); } } @@ -664,29 +649,32 @@ namespace Falcor nameToIndexMapRef.insert(std::make_pair(pinUIData.mPinName, static_cast(guiPinID) )); } - void RenderPassUI::renderPinUI(Gui* pGui, const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index, bool input) + void RenderPassUI::renderPinUI(const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index, bool input) { RenderPassUI::PinUI& pinUI = input ? mInputPins[index] : mOutputPins[index]; - size_t fieldIndex = 0; + size_t fieldIndex = -1; for (size_t i = 0; i < mReflection.getFieldCount(); ++i) { - if (mReflection.getField(i).getName() == pinUI.mPinName) + if (mReflection.getField(i)->getName() == pinUI.mPinName) { fieldIndex = i; break; } } - - // only render ui if is input or output - const RenderPassReflection::Field& field = mReflection.getField(fieldIndex); - if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input) || is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output)) + + if (fieldIndex != -1) { - pinUI.renderUI(pGui, field, pGraphUI, passName); + // only render ui if is input or output + const RenderPassReflection::Field& field = *mReflection.getField(fieldIndex); + if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Input) || is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output)) + { + pinUI.renderUI(field, pGraphUI, passName); + } } } - void RenderPassUI::PinUI::renderFieldInfo(Gui* pGui, const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName, const std::string& fieldName) + void RenderPassUI::PinUI::renderFieldInfo(const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName, const std::string& fieldName) { RenderPassReflection::Field::Visibility type = field.getVisibility(); uint32_t isInput = is_set(type, RenderPassReflection::Field::Visibility::Input); @@ -695,68 +683,91 @@ namespace Falcor if (isExecutionPin) { - pGui->addText("Execution Pin"); + ImGui::TextUnformatted("Execution Pin"); return; } - pGui->addText((std::string("Field Name: ") + fieldName).c_str(), true); + ImGui::SameLine(); + ImGui::TextUnformatted((std::string("Field Name: ") + fieldName).c_str()); ImGui::Separator(); - pGui->addText((field.getDesc() + "\n\n").c_str()); + ImGui::TextUnformatted((field.getDesc() + "\n\n").c_str()); - pGui->addText("ResourceFlags : "); + ImGui::TextUnformatted("ResourceFlags : "); - if (isInput && isOutput) pGui->addText("InputOutput", true); - else if (isInput) pGui->addText("Input", true); - else if (isOutput) pGui->addText("Output", true); + if (isInput && isOutput) + { + ImGui::SameLine(); + ImGui::TextUnformatted("InputOutput"); + } + else if (isInput) + { + ImGui::SameLine(); + ImGui::TextUnformatted("Input"); + } + else if (isOutput) + { + ImGui::SameLine(); + ImGui::TextUnformatted("Output"); + } - pGui->addText("ResourceType : "); - pGui->addText(to_string(field.getType()).c_str(), true); + ImGui::TextUnformatted("ResourceType : "); + ImGui::SameLine(); + ImGui::TextUnformatted(to_string(field.getType()).c_str()); - pGui->addText("Width: "); - pGui->addText(std::to_string(field.getWidth()).c_str(), true); + ImGui::TextUnformatted("Width: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getWidth()).c_str()); - pGui->addText("Height: "); - pGui->addText(std::to_string(field.getHeight()).c_str(), true); + ImGui::TextUnformatted("Height: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getHeight()).c_str()); - pGui->addText("Depth: "); - pGui->addText(std::to_string(field.getDepth()).c_str(), true); + ImGui::TextUnformatted("Depth: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getDepth()).c_str()); - pGui->addText("Sample Count: "); - pGui->addText(std::to_string(field.getSampleCount()).c_str(), true); + ImGui::TextUnformatted("Sample Count: "); + ImGui::SameLine(); + ImGui::TextUnformatted(std::to_string(field.getSampleCount()).c_str()); - pGui->addText("ResourceFormat: "); - pGui->addText(to_string(field.getFormat()).c_str(), true); + ImGui::TextUnformatted("ResourceFormat: "); + ImGui::SameLine(); + ImGui::TextUnformatted(to_string(field.getFormat()).c_str()); - pGui->addText("BindFlags: "); - pGui->addText(to_string(field.getBindFlags()).c_str(), true); + ImGui::TextUnformatted("BindFlags: "); + ImGui::SameLine(); + ImGui::TextUnformatted(to_string(field.getBindFlags()).c_str()); - pGui->addText("Flags: "); + ImGui::TextUnformatted("Flags: "); switch (field.getFlags()) { case RenderPassReflection::Field::Flags::None: - pGui->addText("None", true); + ImGui::SameLine(); + ImGui::TextUnformatted("None"); break; case RenderPassReflection::Field::Flags::Optional: - pGui->addText("Optional", true); + ImGui::SameLine(); + ImGui::TextUnformatted("Optional"); break; case RenderPassReflection::Field::Flags::Persistent: - pGui->addText("Persistent", true); + ImGui::SameLine(); + ImGui::TextUnformatted("Persistent"); break; default: should_not_get_here(); } } - void RenderPassUI::PinUI::renderUI(Gui* pGui, const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName) + void RenderPassUI::PinUI::renderUI(const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName) { - pGui->addText(mIsGraphOutput ? "Graph Output : " : ""); - renderFieldInfo(pGui, field, pGraphUI, passName, mPinName); + ImGui::TextUnformatted(mIsGraphOutput ? "Graph Output : " : ""); + renderFieldInfo(field, pGraphUI, passName, mPinName); bool isExecutionPin = mPinName[0] == '#'; if (!isExecutionPin && is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output)) { bool isGraphOut = mIsGraphOutput; - if (pGui->addCheckBox("Graph Output", mIsGraphOutput)) + if (ImGui::Checkbox("Graph Output", &mIsGraphOutput)) { if (isGraphOut && !mIsGraphOutput) { @@ -772,7 +783,7 @@ namespace Falcor ImGui::Separator(); } - void RenderGraphUI::renderPopupMenu(Gui* pGui) + void RenderGraphUI::renderPopupMenu() { bool isPopupOpen = false; bool first = false; @@ -802,7 +813,7 @@ namespace Falcor { const std::string& passName = mpNodeGraphEditor->getPopupNode()->getName(); RenderPassUI& renderPassUI = mRenderPassUI[passName]; - renderPassUI.renderPinUI(pGui, passName, this, mpNodeGraphEditor->getPopupPinIndex(), mpNodeGraphEditor->isPopupPinInput()); + renderPassUI.renderPinUI(passName, this, mpNodeGraphEditor->getPopupPinIndex(), mpNodeGraphEditor->isPopupPinInput()); ImGui::Separator(); } @@ -811,7 +822,7 @@ namespace Falcor ImGui::NodeLink& selectedLink = mpNodeGraphEditor->getLink(mpNodeGraphEditor->selectedLink); std::string srcPassName = std::string(selectedLink.InputNode->getName()); std::string dstPassName = std::string(selectedLink.OutputNode->getName()); - pGui->addText((std::string("Edge: ") + srcPassName + '-' + dstPassName).c_str()); + ImGui::TextUnformatted((std::string("Edge: ") + srcPassName + '-' + dstPassName).c_str()); std::string inputName = std::string(static_cast(selectedLink.OutputNode)->getInputName(selectedLink.OutputSlot)); std::string inputString = dstPassName + (inputName.empty() ? "" : ".") + inputName; uint32_t linkID = mInputPinStringToLinkID[inputString]; @@ -824,20 +835,23 @@ namespace Falcor if (edgeData.srcField.size()) { - pGui->addText("Src Field : "); - pGui->addText(edgeData.srcField.c_str(), true); + ImGui::TextUnformatted("Src Field : "); + ImGui::SameLine(); + ImGui::TextUnformatted(edgeData.srcField.c_str()); } if (edgeData.dstField.size()) { - pGui->addText("Dst Field : "); - pGui->addText(edgeData.dstField.c_str(), true); + ImGui::TextUnformatted("Dst Field : "); + ImGui::SameLine(); + ImGui::TextUnformatted(edgeData.dstField.c_str()); } if (edgeData.dstField.size() || edgeData.srcField.size()) { - pGui->addText("Auto-Generated : "); - pGui->addText(edgeData.autoGenerated ? "true" : "false", true); + ImGui::TextUnformatted("Auto-Generated : "); + ImGui::SameLine(); + ImGui::TextUnformatted(edgeData.autoGenerated ? "true" : "false"); } } } @@ -847,19 +861,10 @@ namespace Falcor ImGui::PopStyleVar(); } - void RenderGraphUI::renderUI(Gui* pGui) + void RenderGraphUI::renderUI(RenderContext* pContext, Gui *pGui) { static std::string dragAndDropText; - - if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ChildWindows)) - { - ImGui::GetIO().FontAllowUserScaling = true; - } - else - { - ImGui::GetIO().FontAllowUserScaling = false; - } - + ImGui::GetIO().FontAllowUserScaling = true; // FIXME mpNodeGraphEditor->mpRenderGraphUI = this; mpNodeGraphEditor->setCurrentGui(pGui); mpNodeGraphEditor->show_top_pane = false; @@ -891,40 +896,41 @@ namespace Falcor mpNodeGraphEditor->getSelectedNodes(selectedNodes); // push update commands for the open pop-up - pGui->pushWindow("Render UI"); + Gui::Window renderWindow(pGui, "Render UI", { 250, 200 }); for (uint32_t i = 0 ; i < static_cast(selectedNodes.size()); ++i) { std::string renderUIName = selectedNodes.Data[i]->getName(); - if (pGui->beginGroup(renderUIName.c_str(), true)) + + auto renderGroup = Gui::Group(pGui, renderUIName, true); + if (renderGroup.open()) { auto pPass = mpRenderGraph->getPass(renderUIName); bool internalResources = false; - - pGui->addSeparator(); - std::string wrappedText = std::string("Description: ") + RenderPassLibrary::getClassDescription(pPass->getName()); + + renderGroup.separator(); + std::string wrappedText = std::string("Description: ") + RenderPassLibrary::getClassDescription(getClassTypeName(pPass.get())); ImGui::TextWrapped("%s", wrappedText.c_str()); - pGui->addSeparator(); + renderGroup.separator(); - pPass->renderUI(pGui, nullptr); + pPass->renderUI(renderGroup); for (uint32_t i = 0; i < mRenderPassUI[renderUIName].mReflection.getFieldCount(); ++i) { - const auto& field = mRenderPassUI[renderUIName].mReflection.getField(i); + const auto& field = *mRenderPassUI[renderUIName].mReflection.getField(i); if (is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Internal)) { if (!internalResources) { - pGui->addSeparator(); pGui->addText("\n\n"); pGui->addSeparator(); - pGui->addText("Internal Resources:"); - pGui->addText("\n\n"); - pGui->addSeparator(); + renderGroup.separator(); renderGroup.text("\n\n"); renderGroup.separator(); + renderGroup.text("Internal Resources:"); + renderGroup.text("\n\n"); + renderGroup.separator(); } - RenderPassUI::PinUI::renderFieldInfo(pGui, field, this, renderUIName, field.getName()); + RenderPassUI::PinUI::renderFieldInfo(field, this, renderUIName, field.getName()); internalResources = true; } } - if (internalResources) pGui->addSeparator(); - + if (internalResources) renderGroup.separator(); // TODO -- only call this with data change if (ImGui::IsWindowFocused()) { @@ -932,15 +938,15 @@ namespace Falcor } mShouldUpdate = true; - pGui->endGroup(); + renderGroup.release(); } } - pGui->popWindow(); + renderWindow.release(); if (mpNodeGraphEditor->getPopupPinIndex() != uint32_t(-1) || (mpNodeGraphEditor->selectedLink != -1)) { - renderPopupMenu(pGui); + renderPopupMenu(); } else { @@ -956,7 +962,21 @@ namespace Falcor std::string statement; bool addPass = false; - if (pGui->dragDropDest("RenderPassType", statement)) + bool b = false; + if (ImGui::BeginDragDropTarget()) + { + auto dragDropPayload = ImGui::AcceptDragDropPayload("RenderPassType"); + b = dragDropPayload && dragDropPayload->IsDataType("RenderPassType") && (dragDropPayload->Data != nullptr); + if (b) + { + statement.resize(dragDropPayload->DataSize); + std::memcpy(&statement.front(), dragDropPayload->Data, dragDropPayload->DataSize); + } + + ImGui::EndDragDropTarget(); + } + + if (b) { dragAndDropText = statement; mNewNodeStartPosition = { -mpNodeGraphEditor->offset.x + mousePos.x, -mpNodeGraphEditor->offset.y + mousePos.y }; @@ -969,20 +989,19 @@ namespace Falcor if (mDisplayDragAndDropPopup) { - pGui->pushWindow("CreateNewGraph", 256, 128, - static_cast(mousePos.x), static_cast(mousePos.y)); + Gui::Window createGraphWindow(pGui, "CreateNewGraph", mDisplayDragAndDropPopup, { 256, 128 }, { (uint32_t)mousePos.x, (uint32_t)mousePos.y }); - pGui->addTextBox("Pass Name", mNextPassName); - if (pGui->addButton("create##renderpass")) + createGraphWindow.textbox("Pass Name", mNextPassName); + if (createGraphWindow.button("create##renderpass")) { addPass = true; } - if (pGui->addButton("cancel##renderPass")) + if (createGraphWindow.button("cancel##renderPass")) { mDisplayDragAndDropPopup = false; } - pGui->popWindow(); + createGraphWindow.release(); } if (addPass) @@ -1000,12 +1019,12 @@ namespace Falcor } // set editor window behind other windows. set top menu bar to always be infront.this is repeated for other render call - ImGui::BringWindowToBack(ImGui::FindWindowByName("Graph Editor")); - ImGui::BringWindowToFront(ImGui::FindWindowByName("##MainMenuBar")); + ImGui::BringWindowToDisplayBack(ImGui::FindWindowByName("Graph Editor")); + ImGui::BringWindowToDisplayFront(ImGui::FindWindowByName("##MainMenuBar")); return; } - updateDisplayData(); + updateDisplayData(pContext); mAllNodeTypes.clear(); @@ -1062,8 +1081,8 @@ namespace Falcor updatePins(); mpNodeGraphEditor->render(); - ImGui::BringWindowToBack(ImGui::FindWindowByName("Graph Editor")); - ImGui::BringWindowToFront(ImGui::FindWindowByName("##MainMenuBar")); + ImGui::BringWindowToDisplayBack(ImGui::FindWindowByName("Graph Editor")); + ImGui::BringWindowToDisplayFront(ImGui::FindWindowByName("##MainMenuBar")); } void RenderGraphUI::reset() @@ -1133,10 +1152,10 @@ namespace Falcor { std::string srcString = currentPass.first + "." + currentPinName; std::string dstString = std::string(pNode->getName()) + "." + dstName; - const RenderPassReflection::Field& srcPin = currentPassUI.mReflection.getField(currentPinName); - const RenderPassReflection::Field& dstPin = mRenderPassUI[pNode->getName()].mReflection.getField(dstName); + const RenderPassReflection::Field& srcPin = *currentPassUI.mReflection.getField(currentPinName); + const RenderPassReflection::Field& dstPin = *mRenderPassUI[pNode->getName()].mReflection.getField(dstName); - if (mpRenderGraph->canAutoResolve(srcPin, dstPin)) + if (false/*mpRenderGraph->canAutoResolve(srcPin, dstPin)*/) { mLogString += std::string("Warning: Edge ") + srcString + " - " + dstName + " can auto-resolve.\n"; edgeColor = kAutoResolveEdgesColor; @@ -1223,49 +1242,41 @@ namespace Falcor const float offsetY = 128.0f; glm::vec2 newNodePosition = mNewNodeStartPosition; - if (!mpRenderGraph->mExecutionList.size()) - { - newNodePosition.x += offsetX * nodeID; - } - else + auto topologicalSort = DirectedGraphTopologicalSort::sort(mpRenderGraph->mpGraph.get()); + + // For each object in the vector, if it's being used in the execution, put it in the list + for (auto& node : topologicalSort) { - for (const auto& passID : mpRenderGraph->mExecutionList) + newNodePosition.x += offsetX; + + if (node == nodeID) { - newNodePosition.x += offsetX; + const DirectedGraph::Node* pNode = mpRenderGraph->mpGraph->getNode(nodeID); + if (!pNode->getIncomingEdgeCount()) + { + newNodePosition.y += offsetY * pNode->getIncomingEdgeCount() * nodeID; + break; + } - if (passID == nodeID) + for (uint32_t i = 0; i < pNode->getIncomingEdgeCount(); ++i) { - const DirectedGraph::Node* pNode = mpRenderGraph->mpGraph->getNode(nodeID); - if (!pNode->getIncomingEdgeCount()) + uint32_t outgoingEdgeCount = mpRenderGraph->mpGraph->getNode(mpRenderGraph->mpGraph->getEdge(pNode->getIncomingEdge(i))->getSourceNode())->getOutgoingEdgeCount(); + if (outgoingEdgeCount > pNode->getIncomingEdgeCount()) { - newNodePosition.y += offsetY * pNode->getIncomingEdgeCount() * passID; + // move down by index in + newNodePosition.y += offsetY * (outgoingEdgeCount - pNode->getIncomingEdgeCount()); break; } - - for (uint32_t i = 0; i < pNode->getIncomingEdgeCount(); ++i) - { - uint32_t outgoingEdgeCount = mpRenderGraph->mpGraph->getNode(mpRenderGraph->mpGraph->getEdge(pNode->getIncomingEdge(i))->getSourceNode())->getOutgoingEdgeCount(); - if (outgoingEdgeCount > pNode->getIncomingEdgeCount()) - { - // move down by index in - newNodePosition.y += offsetY * (outgoingEdgeCount - pNode->getIncomingEdgeCount()); - break; - } - } - - break; } + break; } } - if (newNodePosition.x > mMaxNodePositionX) - { - mMaxNodePositionX = newNodePosition.x; - } + if (newNodePosition.x > mMaxNodePositionX) mMaxNodePositionX = newNodePosition.x; return newNodePosition; } - void RenderGraphUI::updateDisplayData() + void RenderGraphUI::updateDisplayData(RenderContext* pContext) { uint32_t nodeIndex = 0; @@ -1283,8 +1294,7 @@ namespace Falcor previousGuiNodeIDs.insert(std::make_pair(currentRenderPassUI.first, currentRenderPassUI.second.mGuiNodeID)); } - mpRenderGraph->resolveExecutionOrder(); - + mpRenderGraph->compile(pContext); mRenderPassUI.clear(); mInputPinStringToLinkID.clear(); @@ -1316,7 +1326,7 @@ namespace Falcor } // clear and rebuild reflection for each pass. - renderPassUI.mReflection = mpRenderGraph->mNodeData[nameToIndex.second].pPass->reflect(); + renderPassUI.mReflection = mpRenderGraph->mNodeData[nameToIndex.second].pPass->reflect({}); // test to see if we have hit a graph output std::unordered_set passGraphOutputs; @@ -1342,10 +1352,10 @@ namespace Falcor while (pinIndex < renderPassUI.mReflection.getFieldCount()) { - bool isInput = is_set(renderPassUI.mReflection.getField(pinIndex).getVisibility(),RenderPassReflection::Field::Visibility::Input); + bool isInput = is_set(renderPassUI.mReflection.getField(pinIndex)->getVisibility(),RenderPassReflection::Field::Visibility::Input); if (isInput) { - if (renderPassUI.mReflection.getField(pinIndex).getName() == currentEdge.dstField) { break; } + if (renderPassUI.mReflection.getField(pinIndex)->getName() == currentEdge.dstField) { break; } inputPinIndex++; } pinIndex++; @@ -1357,14 +1367,14 @@ namespace Falcor std::string dstFieldName = currentEdge.dstField.empty() ? kInPrefix + nameToIndex.first : currentEdge.dstField; std::string inputPinString = nameToIndex.first + "." + dstFieldName; - std::string srcFieldName = currentEdge.srcField.empty() ? kOutPrefix + pSourceNode->second.nodeName : currentEdge.srcField; - std::string outputPinString = pSourceNode->second.nodeName + "." + srcFieldName; + std::string srcFieldName = currentEdge.srcField.empty() ? kOutPrefix + pSourceNode->second.name : currentEdge.srcField; + std::string outputPinString = pSourceNode->second.name + "." + srcFieldName; if (nodeConnectedInput.find(inputPinString) == nodeConnectedInput.end()) { nodeConnectedInput.insert(inputPinString); } - renderPassUI.addUIPin(dstFieldName, inputPinIndex, true, srcFieldName, pSourceNode->second.nodeName); + renderPassUI.addUIPin(dstFieldName, inputPinIndex, true, srcFieldName, pSourceNode->second.name); mOutputToInputPins[outputPinString].push_back(std::make_pair(inputPinIndex, renderPassUI.mGuiNodeID)); mInputPinStringToLinkID.insert(std::make_pair(inputPinString, edgeID)); } @@ -1387,10 +1397,10 @@ namespace Falcor while (pinIndex < renderPassUI.mReflection.getFieldCount()) { - bool isOutput = (static_cast(renderPassUI.mReflection.getField(pinIndex).getVisibility() & RenderPassReflection::Field::Visibility::Output) != 0); + bool isOutput = (static_cast(renderPassUI.mReflection.getField(pinIndex)->getVisibility() & RenderPassReflection::Field::Visibility::Output) != 0); if (isOutput) { - if (renderPassUI.mReflection.getField(pinIndex).getName() == currentEdge.srcField) { break; } + if (renderPassUI.mReflection.getField(pinIndex)->getName() == currentEdge.srcField) { break; } outputPinIndex++; } pinIndex++; @@ -1400,12 +1410,12 @@ namespace Falcor assert(pDestNode != mpRenderGraph->mNodeData.end()); addedExecutionOutput = currentEdge.dstField.empty(); - std::string dstFieldName = currentEdge.dstField.empty() ? kInPrefix + pDestNode->second.nodeName : currentEdge.dstField; + std::string dstFieldName = currentEdge.dstField.empty() ? kInPrefix + pDestNode->second.name : currentEdge.dstField; std::string inputPinString = nameToIndex.first + "." + dstFieldName; std::string srcFieldName = currentEdge.srcField.empty() ? kOutPrefix + nameToIndex.first : currentEdge.srcField; - std::string outputPinString = pDestNode->second.nodeName + "." + srcFieldName; + std::string outputPinString = pDestNode->second.name + "." + srcFieldName; - renderPassUI.addUIPin(srcFieldName, outputPinIndex, false, dstFieldName, pDestNode->second.nodeName, isGraphOutput); + renderPassUI.addUIPin(srcFieldName, outputPinIndex, false, dstFieldName, pDestNode->second.name, isGraphOutput); nodeConnectedOutput.insert(pinString); } @@ -1415,7 +1425,7 @@ namespace Falcor for (uint32_t i = 0; i < renderPassUI.mReflection.getFieldCount(); ++i) { - const auto& currentField = renderPassUI.mReflection.getField(i); + const auto& currentField = *renderPassUI.mReflection.getField(i); if (is_set(currentField.getVisibility(), RenderPassReflection::Field::Visibility::Input)) { diff --git a/Framework/Source/Experimental/RenderGraph/RenderGraphUI.h b/Source/Falcor/RenderGraph/RenderGraphUI.h similarity index 87% rename from Framework/Source/Experimental/RenderGraph/RenderGraphUI.h rename to Source/Falcor/RenderGraph/RenderGraphUI.h index 0fb80e740..95f342e53 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderGraphUI.h +++ b/Source/Falcor/RenderGraph/RenderGraphUI.h @@ -26,25 +26,21 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderGraph.h" -#include "Experimental/RenderGraph/RenderPass.h" -#include "Experimental/RenderGraph/RenderPassReflection.h" -#include "Experimental/RenderGraph/RenderGraphIR.h" -#include "Experimental/RenderGraph/RenderGraphScripting.h" -#include -#include +#include "RenderPassReflection.h" +#include "RenderGraph.h" +#include "RenderGraphIR.h" namespace Falcor { - class RenderGraphUI; + class NodeGraphEditorGui; - class RenderPassUI + class dlldecl RenderPassUI { public: // wrapper around inserting new pin for a given pass void addUIPin(const std::string& fieldName, uint32_t guiPinID, bool isInput, const std::string& connectedPinName = "", const std::string& connectedNodeName = "", bool isGraphOutput = false); - void renderPinUI(Gui* pGui, const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index = 0, bool input = false); + void renderPinUI(const std::string& passName, RenderGraphUI* pGraphUI, uint32_t index = 0, bool input = false); friend class RenderGraphUI; @@ -59,8 +55,8 @@ namespace Falcor std::string mConnectedNodeName; bool mIsGraphOutput; - static void renderFieldInfo(Gui* pGui, const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName, const std::string& fieldName); - void renderUI(Gui* pGui, const RenderPassReflection::Field& field, RenderGraphUI* graphUI, const std::string& passName); + static void renderFieldInfo(const RenderPassReflection::Field& field, RenderGraphUI* pGraphUI, const std::string& passName, const std::string& fieldName); + void renderUI(const RenderPassReflection::Field& field, RenderGraphUI* graphUI, const std::string& passName); }; std::vector mInputPins; @@ -74,7 +70,7 @@ namespace Falcor uint32_t mNumExecutionPins = 0; }; - class RenderGraphUI + class dlldecl RenderGraphUI { public: RenderGraphUI(); @@ -85,7 +81,7 @@ namespace Falcor /** Display enter graph in GUI. */ - void renderUI(Gui *pGui); + void renderUI(RenderContext* pContext, Gui *pGui); /** Clear graph ui for rebuilding node graph */ @@ -97,7 +93,7 @@ namespace Falcor /** Writes out all the changes made to the graph */ - void writeUpdateScriptToFile(const std::string& filePath, float lastFrameTimes); + void writeUpdateScriptToFile(RenderContext* pContext, const std::string& filePath, float lastFrameTimes); /** function used to add an edge for the internally referenced render graph and update ui data */ @@ -145,7 +141,7 @@ namespace Falcor /** Update change for the graph based on script */ - void updateGraph(); + void updateGraph(RenderContext* pContext); /** Get name of reference graph */ @@ -158,7 +154,7 @@ namespace Falcor /** Updates structure for drawing the GUI graph */ - void updateDisplayData(); + void updateDisplayData(RenderContext* pContext); /** Updates information about pin connections and graph output. */ @@ -170,7 +166,7 @@ namespace Falcor /** Renders specialized pop up menu. */ - void renderPopupMenu(Gui* pGui); + void renderPopupMenu(); /** Displays pop-up message if can auto resolve on an edge */ diff --git a/Framework/Source/Experimental/RenderGraph/RenderPass.cpp b/Source/Falcor/RenderGraph/RenderPass.cpp similarity index 80% rename from Framework/Source/Experimental/RenderGraph/RenderPass.cpp rename to Source/Falcor/RenderGraph/RenderPass.cpp index eb98fb04f..df97d1883 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderPass.cpp +++ b/Source/Falcor/RenderGraph/RenderPass.cpp @@ -25,22 +25,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RenderPass.h" -#include "Experimental/RenderGraph/ResourceCache.h" namespace Falcor { - RenderData::RenderData(const std::string& passName, const std::shared_ptr& pResourceCache, const Dictionary::SharedPtr& pDict) + RenderData::RenderData(const std::string& passName, const ResourceCache::SharedPtr& pResourceCache, const Dictionary::SharedPtr& pDict, const uvec2& defaultTexDims, ResourceFormat defaultTexFormat) : mName(passName) , mpResources(pResourceCache) , mpDictionary(pDict) + , mDefaultTexDims(defaultTexDims) + , mDefaultTexFormat(defaultTexFormat) { if (!mpDictionary) mpDictionary = Dictionary::create(); } - Falcor::Texture::SharedPtr RenderData::getTexture(const std::string& name) const + const Resource::SharedPtr& RenderData::getResource(const std::string& name) const { - return std::dynamic_pointer_cast(mpResources->getResource(mName + '.' + name)); + return mpResources->getResource(mName + '.' + name); } } diff --git a/Framework/Source/Experimental/RenderGraph/RenderPass.h b/Source/Falcor/RenderGraph/RenderPass.h similarity index 51% rename from Framework/Source/Experimental/RenderGraph/RenderPass.h rename to Source/Falcor/RenderGraph/RenderPass.h index dfd1578b3..9c5c75a4d 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderPass.h +++ b/Source/Falcor/RenderGraph/RenderPass.h @@ -26,48 +26,84 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Utils/Dictionary.h" -#include "Experimental/RenderGraph/RenderPassReflection.h" +#include "RenderPassReflection.h" +#include "Utils/Scripting/Dictionary.h" +#include "ResourceCache.h" +#include "Core/API/Texture.h" +#include "Scene/Scene.h" +#include "Utils/UI/Gui.h" +#include "Utils/UI/UserInput.h" +#include "Core/API/RenderContext.h" namespace Falcor { - class Scene; - class Gui; - class Texture; - class RenderContext; - class ResourceCache; - - class RenderData + /** Helper class that's passed to the user during `RenderPass::execute()` + */ + class dlldecl RenderData { public: - RenderData(const std::string& passName, const std::shared_ptr& pResourceCache, const Dictionary::SharedPtr& pDict); + /** Get a resource + \param[in] name The name of the pass' resource (i.e. "outputColor"). No need to specify the pass' name + \return If the name exists, a pointer to the resource. Otherwise, nullptr + */ + const Resource::SharedPtr& operator[](const std::string& name) const { return getResource(name); } - std::shared_ptr getTexture(const std::string& name) const; + /** Get a resource + \param[in] name The name of the pass' resource (i.e. "outputColor"). No need to specify the pass' name + \return If the name exists, a pointer to the resource. Otherwise, nullptr + */ + const Resource::SharedPtr& getResource(const std::string& name) const; + + /** Get the global dictionary. You can use it to pass data between different passes + */ Dictionary& getDictionary() const { return (*mpDictionary); } + /** Get the default dimensions used for Texture2Ds (when `0` is specified as the dimensions in `RenderPassReflection`) + */ + const uvec2& getDefaultTextureDims() const { return mDefaultTexDims; } + + /** Get the default format used for Texture2Ds (when `Unknown` is specified as the format in `RenderPassReflection`) + */ + ResourceFormat getDefaultTextureFormat() const { return mDefaultTexFormat; } protected: + friend class RenderGraphExe; + RenderData(const std::string& passName, const ResourceCache::SharedPtr& pResourceCache, const Dictionary::SharedPtr& pDict, const uvec2& defaultTexDims, ResourceFormat defaultTexFormat); const std::string& mName; - std::shared_ptr mpResources; + ResourceCache::SharedPtr mpResources; Dictionary::SharedPtr mpDictionary; + uvec2 mDefaultTexDims; + ResourceFormat mDefaultTexFormat; }; - class RenderPass : public std::enable_shared_from_this + class dlldecl RenderPass : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; + virtual ~RenderPass() = default; + + struct CompileData + { + RenderPassReflection connectedResources; + uvec2 defaultTexDims; + ResourceFormat defaultTexFormat; + }; /** Called once before compilation. Describes I/O requirements of the pass. The requirements can't change after the graph is compiled. If the IO requests are dynamic, you'll need to trigger compilation of the render-graph yourself. */ - virtual RenderPassReflection reflect() const = 0; + virtual RenderPassReflection reflect(const CompileData& compileData) = 0; + + /** Will be called during graph compilation. You should throw an exception in case the compilation failed + */ + virtual void compile(RenderContext* pContext, const CompileData& compileData) {} /** Executes the pass. */ - virtual void execute(RenderContext* pRenderContext, const RenderData* pData) = 0; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) = 0; /** Get a dictionary that can be used to reconstruct the object */ - virtual Dictionary getScriptingDictionary() const { return {}; } + virtual Dictionary getScriptingDictionary() { return {}; } /** Get a string describing what the pass is doing */ @@ -75,15 +111,11 @@ namespace Falcor /** Render the pass's UI */ - virtual void renderUI(Gui* pGui, const char* uiGroup) {} - - /** Will be called whenever the backbuffer size changed - */ - virtual void onResize(uint32_t width, uint32_t height) {} + virtual void renderUI(Gui::Widgets& widget) {} /** Set a scene into the render-pass */ - virtual void setScene(const std::shared_ptr& pScene) {} + virtual void setScene(RenderContext* pRenderContext, const std::shared_ptr& pScene) {} /** Mouse event handler. Returns true if the event was handled by the object, false otherwise @@ -91,26 +123,18 @@ namespace Falcor virtual bool onMouseEvent(const MouseEvent& mouseEvent) { return false; } /** Keyboard event handler - Returns true if the event was handled by the object, false otherwise + Returns true if the event was handled by the object, false otherwise */ virtual bool onKeyEvent(const KeyboardEvent& keyEvent) { return false; } - /** Get the pass' name + /** Get the current pass' name as defined in the graph */ const std::string& getName() const { return mName; } - using PassChangedCallback = std::function; - - /** Set the callback function - */ - void setPassChangedCB(PassChangedCallback cb) { mPassChangedCB = cb; } protected: - RenderPass(const std::string& className) : mName(className) - { - auto cb = [] {}; - mPassChangedCB = cb; - } + friend class RenderGraph; + RenderPass() = default; std::string mName; - PassChangedCallback mPassChangedCB; + std::function mPassChangedCB = [] {}; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Model/ModelRenderer.cpp b/Source/Falcor/RenderGraph/RenderPassHelpers.h similarity index 72% rename from Framework/Source/Graphics/Model/ModelRenderer.cpp rename to Source/Falcor/RenderGraph/RenderPassHelpers.h index db1377c81..ccfd53f74 100644 --- a/Framework/Source/Graphics/Model/ModelRenderer.cpp +++ b/Source/Falcor/RenderGraph/RenderPassHelpers.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "ModelRenderer.h" -#include "Graphics/Scene/SceneRenderer.h" +#pragma once namespace Falcor { - void ModelRenderer::render(RenderContext* pRenderContext, Model::SharedPtr pModel, Camera* pCamera, bool frustumCulling) + /** Helper struct with metadata for a render pass input/output. + */ + struct ChannelDesc { - Scene::SharedPtr pScene = Scene::create(); - pScene->addModelInstance(pModel, ""); + std::string name; ///< Render pass I/O pin name. + std::string texname; ///< Name of corresponding resource in the shader, or empty if it's not a shader variable. + std::string desc; ///< Human-readable description of the data. + bool optional = false; ///< Set to true if the resource is optional. + ResourceFormat format = ResourceFormat::RGBA32Float; + }; - SceneRenderer::SharedPtr pSceneRenderer = SceneRenderer::create(pScene); - pSceneRenderer->toggleMeshCulling(frustumCulling); - pSceneRenderer->renderScene(pRenderContext, pCamera); - } + using ChannelList = std::vector; } diff --git a/Framework/Source/Experimental/RenderGraph/RenderPassLibrary.cpp b/Source/Falcor/RenderGraph/RenderPassLibrary.cpp similarity index 83% rename from Framework/Source/Experimental/RenderGraph/RenderPassLibrary.cpp rename to Source/Falcor/RenderGraph/RenderPassLibrary.cpp index d6d39aa84..7281f8595 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderPassLibrary.cpp +++ b/Source/Falcor/RenderGraph/RenderPassLibrary.cpp @@ -25,31 +25,33 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "RenderPassLibrary.h" -#include "Experimental/RenderPasses/BlitPass.h" -#include "Experimental/RenderPasses/DepthPass.h" -#include "Experimental/RenderPasses/ForwardLightingPass.h" -#include "Experimental/RenderPasses/ResolvePass.h" -#include "Experimental/RenderPasses/ImageLoader.h" +#include "RenderPasses/BlitPass.h" +#include "RenderPasses/DepthPass.h" +#include "RenderPasses/ForwardLightingPass.h" +#include "RenderPasses/ResolvePass.h" +#include "RenderPasses/ImageLoader.h" #include "Effects/SkyBox/SkyBox.h" #include "Effects/Shadows/CSM.h" -#include "Effects/ToneMapping/ToneMapping.h" -#include "Effects/FXAA/FXAA.h" -#include "Effects/AmbientOcclusion/SSAO.h" -#include "Effects/TAA/TAA.h" -#include "API/Device.h" +#include "Effects/ToneMapping/ToneMappingPass.h" +#include "Effects/FXAA/FXAAPass.h" +#include "Effects/AmbientOcclusion/SSAOPass.h" +#include "Effects/TAA/TAAPass.h" +#include "Effects/Utils/GaussianBlurPass.h" +#include "Core/API/Device.h" #include "RenderGraph.h" #include namespace Falcor { + extern std::vector gRenderGraphs; static const std::string kDllPrefix = ".falcor"; RenderPassLibrary* RenderPassLibrary::spInstance = nullptr; template - using PassFunc = typename Pass::SharedPtr(*)(const Dictionary&); + using PassFunc = typename Pass::SharedPtr(*)(RenderContext* pRenderContext, const Dictionary&); #define addClass(c, desc) registerClass(#c, desc, (PassFunc)c::create) @@ -78,13 +80,14 @@ namespace Falcor lib.addClass(ForwardLightingPass, ForwardLightingPass::kDesc); lib.addClass(DepthPass, DepthPass::kDesc); lib.addClass(CascadedShadowMaps, CascadedShadowMaps::kDesc); - lib.addClass(ToneMapping, ToneMapping::kDesc); - lib.addClass(FXAA, FXAA::kDesc); - lib.addClass(SSAO, SSAO::kDesc); - lib.addClass(TemporalAA, TemporalAA::kDesc); + lib.addClass(ToneMappingPass, ToneMappingPass::kDesc); + lib.addClass(FXAAPass, FXAAPass::kDesc); + lib.addClass(SSAOPass, SSAOPass::kDesc); + lib.addClass(TemporalAAPass, TemporalAAPass::kDesc); lib.addClass(SkyBox, SkyBox::kDesc); lib.addClass(ResolvePass, ResolvePass::kDesc); lib.addClass(ImageLoader, ImageLoader::kDesc); + lib.addClass(GaussianBlurPass, GaussianBlurPass::kDesc); return true; }; @@ -109,7 +112,7 @@ namespace Falcor } } - std::shared_ptr RenderPassLibrary::createPass(const char* className, const Dictionary& dict) + std::shared_ptr RenderPassLibrary::createPass(RenderContext* pRenderContext, const char* className, const Dictionary& dict) { #ifdef _MSC_VER static const std::string kDllType = ".dll"; @@ -132,7 +135,7 @@ namespace Falcor } auto& renderPass = mPasses[className]; - return renderPass.func(dict); + return renderPass.func(pRenderContext, dict); } RenderPassLibrary::DescVec RenderPassLibrary::enumerateClasses() const @@ -177,7 +180,7 @@ namespace Falcor if (mLibs.find(fullpath) != mLibs.end()) { - reloadLibrary(fullpath); + logInfo("Render-pass library `" + filename + "` already loaded. Ignoring `loadLibrary()` call"); return; } @@ -233,9 +236,11 @@ namespace Falcor std::remove((filename + kDllPrefix).c_str()); mLibs.erase(libIt); } - - void RenderPassLibrary::reloadLibrary(std::string name) + + void RenderPassLibrary::reloadLibrary(RenderContext* pRenderContext, std::string name) { + assert(pRenderContext); + auto lastTime = getFileModifiedTime(name); if ((lastTime == mLibs[name].lastModified) || (lastTime == 0)) return; @@ -249,10 +254,10 @@ namespace Falcor }; std::vector passesToReplace; - + for (auto& passDesc : mPasses) { - if(passDesc.second.module != module) continue; + if (passDesc.second.module != module) continue; // Go over all the graphs and remove this pass for (auto& pGraph : gRenderGraphs) @@ -260,11 +265,11 @@ namespace Falcor // Loop over the passes for (auto& node : pGraph->mNodeData) { - if (node.second.pPass->getName() == passDesc.first) + if (getClassTypeName(node.second.pPass.get()) == passDesc.first) { passesToReplace.push_back({ pGraph, passDesc.first, node.first }); node.second.pPass = nullptr; - pGraph->mpResourcesCache->reset(); + pGraph->mpExe.reset(); } } } @@ -277,15 +282,15 @@ namespace Falcor // Recreate the passes for (auto& r : passesToReplace) { - r.pGraph->mNodeData[r.nodeId].pPass = createPass(r.className.c_str()); - r.pGraph->mRecompile = true; + r.pGraph->mNodeData[r.nodeId].pPass = createPass(pRenderContext, r.className.c_str()); + r.pGraph->mpExe = nullptr; } } - void RenderPassLibrary::reloadLibraries() + void RenderPassLibrary::reloadLibraries(RenderContext* pRenderContext) { // Copy the libs vector so we don't screw up the iterator auto libs = mLibs; - for (const auto& l : libs) reloadLibrary(l.first); + for (const auto& l : libs) reloadLibrary(pRenderContext, l.first); } -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderPassLibrary.h b/Source/Falcor/RenderGraph/RenderPassLibrary.h similarity index 90% rename from Framework/Source/Experimental/RenderGraph/RenderPassLibrary.h rename to Source/Falcor/RenderGraph/RenderPassLibrary.h index 4b18e581e..b3e182430 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderPassLibrary.h +++ b/Source/Falcor/RenderGraph/RenderPassLibrary.h @@ -26,20 +26,18 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "RenderPassReflection.h" -#include "Utils/Dictionary.h" +#include "Utils/Scripting/Dictionary.h" +#include "RenderPass.h" namespace Falcor { - class RenderPass; - class Device; - class RenderPassLibrary + class dlldecl RenderPassLibrary { public: RenderPassLibrary() = default; RenderPassLibrary(RenderPassLibrary&) = delete; ~RenderPassLibrary(); - using CreateFunc = std::function(const Dictionary&)>; + using CreateFunc = std::function; struct RenderPassDesc { @@ -67,7 +65,7 @@ namespace Falcor /** Instantiate a new render-pass object */ - std::shared_ptr createPass(const char* className, const Dictionary& dict = {}); + RenderPass::SharedPtr createPass(RenderContext* pRenderContext, const char* className, const Dictionary& dict = {}); /** Get a list of all the registered classes */ @@ -83,7 +81,7 @@ namespace Falcor /** Reload libraries */ - void reloadLibraries(); + void reloadLibraries(RenderContext* pRenderContext); /** A render-pass DLL should implement a function called `getPasses` with the following signature */ @@ -120,6 +118,6 @@ namespace Falcor std::unordered_map mLibs; std::unordered_map mPasses; - void reloadLibrary(std::string name); + void reloadLibrary(RenderContext* pRenderContext, std::string name); }; -} \ No newline at end of file +} diff --git a/Source/Falcor/RenderGraph/RenderPassReflection.cpp b/Source/Falcor/RenderGraph/RenderPassReflection.cpp new file mode 100644 index 000000000..4eb590253 --- /dev/null +++ b/Source/Falcor/RenderGraph/RenderPassReflection.cpp @@ -0,0 +1,277 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "RenderPassReflection.h" + +namespace Falcor +{ + RenderPassReflection::Field::Field(const std::string& name, const std::string& desc, Visibility v) : mName(name), mVisibility(v), mDesc(desc) + { + } + + RenderPassReflection::Field& RenderPassReflection::Field::rawBuffer(uint32_t size) + { + mType = Type::RawBuffer; + mWidth = size; + mHeight = mDepth = mArraySize = mMipCount = 0; + return *this; + } + + RenderPassReflection::Field& RenderPassReflection::Field::texture1D(uint32_t width, uint32_t mipCount, uint32_t arraySize) + { + mType = Type::Texture1D; + mWidth = width; + mHeight = 1; + mDepth = 1; + mArraySize = arraySize; + mMipCount = mipCount; + return *this; + } + + RenderPassReflection::Field& RenderPassReflection::Field::texture2D(uint32_t width, uint32_t height, uint32_t sampleCount, uint32_t mipCount, uint32_t arraySize) + { + mType = Type::Texture2D; + mWidth = width; + mHeight = height; + mSampleCount = sampleCount; + mDepth = 1; + mArraySize = arraySize; + mMipCount = mipCount; + return *this; + } + + RenderPassReflection::Field& RenderPassReflection::Field::texture3D(uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize) + { + mType = Type::Texture3D; + mWidth = width; + mHeight = height; + mDepth = depth; + mArraySize = arraySize; + return *this; + } + + RenderPassReflection::Field& RenderPassReflection::Field::textureCube(uint32_t width, uint32_t height, uint32_t mipCount, uint32_t arraySize) + { + mType = Type::TextureCube; + mWidth = width; + mHeight = height; + mDepth = 1; + mMipCount = mipCount; + mArraySize = arraySize; + return *this; + } + + RenderPassReflection::Field& RenderPassReflection::Field::resourceType(RenderPassReflection::Field::Type type, uint32_t width, uint32_t height, uint32_t depth, uint32_t sampleCount, uint32_t mipCount, uint32_t arraySize) + { + switch (type) + { + case RenderPassReflection::Field::Type::RawBuffer: + if(height > 0 || depth > 0 || sampleCount > 0) logWarning("RenderPassReflection::Field::resourceType - height, depth, sampleCount for " + to_string(type) + " must be either 0"); + return rawBuffer(width); + case RenderPassReflection::Field::Type::Texture1D: + if (height > 1 || depth > 1 || sampleCount > 1) logWarning("RenderPassReflection::Field::resourceType - height, depth, sampleCount for " + to_string(type) + " must be either 0 or 1"); + return texture1D(width, mipCount, arraySize); + case RenderPassReflection::Field::Type::Texture2D: + if (depth > 1) logWarning("RenderPassReflection::Field::resourceType - depth for " + to_string(type) + " must be either 0 or 1"); + return texture2D(width, height, sampleCount, mipCount, arraySize); + case RenderPassReflection::Field::Type::Texture3D: + if (sampleCount > 1 || mipCount > 1) logWarning("RenderPassReflection::Field::resourceType - sampleCount, mipCount for " + to_string(type) + " must be either 0 or 1"); + return texture3D(width, height, depth, arraySize); + case RenderPassReflection::Field::Type::TextureCube: + if (depth > 1 || sampleCount > 1) logWarning("RenderPassReflection::Field::resourceType - depth, sampleCount for " + to_string(type) + " must be either 0 or 1"); + return textureCube(width, height, mipCount, arraySize); + default: + throw std::runtime_error("RenderPassReflection::Field::resourceType - " + to_string(type) + " is not a valid Field type"); + } + } + + + RenderPassReflection::Field& RenderPassReflection::Field::format(ResourceFormat f) { mFormat = f; return *this; } + RenderPassReflection::Field& RenderPassReflection::Field::bindFlags(Resource::BindFlags flags) { mBindFlags = flags; return *this; } + RenderPassReflection::Field& RenderPassReflection::Field::flags(Flags flags) { mFlags = flags; return *this; } + RenderPassReflection::Field& RenderPassReflection::Field::visibility(Visibility vis) { mVisibility = vis; return *this; } + RenderPassReflection::Field& RenderPassReflection::Field::name(const std::string& name) { mName = name; return *this; } + RenderPassReflection::Field& RenderPassReflection::Field::desc(const std::string& desc) { mDesc = desc; return *this; } + + bool RenderPassReflection::Field::isValid() const + { + if (mSampleCount > 1 && mMipCount > 1) + { + logError("Trying to create a multisampled RenderPassReflection::Field `" + mName + "` with mip-count larger than 1. This is illegal."); + return false; + } + + if (is_set(mVisibility, Visibility::Internal) && is_set(mFlags, Flags::Optional)) + { + logError("Internal resource can't be optional, since there will never be a graph edge that forces their creation"); + return false; + } + + return true; + } + + RenderPassReflection::Field& RenderPassReflection::addField(const Field& field) + { + // See if the field already exists + for (auto& existingF : mFields) + { + if (existingF.getName() == field.getName()) + { + // We can only merge input and output fields, otherwise override the previous field + bool ioField = is_set(existingF.getVisibility(), Field::Visibility::Input | Field::Visibility::Output); + bool ioRequest = is_set(field.getVisibility(), Field::Visibility::Input | Field::Visibility::Output); + if (ioField && ioRequest) + { + existingF.mVisibility |= field.getVisibility(); + } + else if ((existingF.getVisibility() & field.getVisibility()) != field.getVisibility()) + { + logError("Trying to add an existing field `" + field.getName() + "` to RenderPassReflection, but the visibility flags mismatch. Overriding the previous definition"); + } + return existingF; + } + } + + mFields.push_back(field); + return mFields.back(); + } + + RenderPassReflection::Field& RenderPassReflection::addField(const std::string& name, const std::string& desc, Field::Visibility visibility) + { + return addField(Field(name, desc, visibility)); + } + + RenderPassReflection::Field& RenderPassReflection::addInput(const std::string& name, const std::string& desc) + { + return addField(name, desc, Field::Visibility::Input); + } + + RenderPassReflection::Field& RenderPassReflection::addOutput(const std::string& name, const std::string& desc) + { + return addField(name, desc, Field::Visibility::Output); + } + + RenderPassReflection::Field& RenderPassReflection::addInputOutput(const std::string& name, const std::string& desc) + { + return addField(name, desc, Field::Visibility::Input | Field::Visibility::Output); + } + + RenderPassReflection::Field& RenderPassReflection::addInternal(const std::string& name, const std::string& desc) + { + return addField(name, desc, Field::Visibility::Internal); + } + + const RenderPassReflection::Field* RenderPassReflection::getField(const std::string& name) const + { + for (const auto& field : mFields) + { + if (field.getName() == name) return &field; + } + return nullptr; + } + + RenderPassReflection::Field& RenderPassReflection::Field::merge(const RenderPassReflection::Field& other) + { + auto err = [&](const std::string& msg) + { + const std::string s = "Can't merge RenderPassReflection::Fields. base(" + getName() + "), newField(" + other.getName() + "). "; + throw std::exception((s + msg).c_str()); + }; + + if (mType != other.mType) err("mismatching types"); + + // Default to base dimension + // If newField property is not 0, retrieve value from newField + // If both newField and base property is specified, generate warning. + auto mf = [err](auto& mine, const auto& other, const std::string& name) + { + auto none = std::remove_reference_t(0); + if (other != none) + { + if (mine == none) mine = other; + else if (mine != other) err(name + " already specified with a mismatching value in a different pass"); + } + }; + +#define merge_field(f) mf(m##f, other.m##f, #f) + merge_field(Width); + merge_field(Height); + merge_field(Depth); + merge_field(ArraySize); + merge_field(MipCount); + merge_field(SampleCount); + merge_field(Format); +#undef merge_field + + assert(is_set(other.mVisibility, RenderPassReflection::Field::Visibility::Internal) == false); // We can't alias/merge internal fields + assert(is_set(mVisibility, RenderPassReflection::Field::Visibility::Internal) == false); // We can't alias/merge internal fields + mVisibility = mVisibility | other.mVisibility; + mBindFlags = mBindFlags | other.mBindFlags; + return *this; + } + + bool RenderPassReflection::Field::operator==(const Field& other) const + { +#define check(_f) if(_f != other._f) return false + + check(mType); + check(mName); + check(mDesc); + check(mWidth); + check(mHeight); + check(mDepth); + check(mSampleCount); + check(mMipCount); + check(mArraySize); + check(mFormat); + check(mBindFlags); + check(mFlags); + check(mVisibility); +#undef check + return true; + } + + bool RenderPassReflection::operator==(const RenderPassReflection& other) const + { + if (other.mFields.size() != mFields.size()) return false; + auto findField = [](const std::string& name, const auto& fields) -> std::optional + { + for (auto& f : fields) if (f.mName == name) return f; + return {}; + }; + + for (const auto& f : mFields) + { + auto otherF = findField(f.mName, other.mFields); + if (!otherF) return false; + if (otherF.value() != f) return false; + } + + return true; + } +} diff --git a/Framework/Source/Experimental/RenderGraph/RenderPassReflection.h b/Source/Falcor/RenderGraph/RenderPassReflection.h similarity index 68% rename from Framework/Source/Experimental/RenderGraph/RenderPassReflection.h rename to Source/Falcor/RenderGraph/RenderPassReflection.h index 3cf074e1a..e1c21ec92 100644 --- a/Framework/Source/Experimental/RenderGraph/RenderPassReflection.h +++ b/Source/Falcor/RenderGraph/RenderPassReflection.h @@ -29,10 +29,10 @@ namespace Falcor { - class RenderPassReflection + class dlldecl RenderPassReflection { public: - class Field + class dlldecl Field { public: /** The type of visibility the field has @@ -61,7 +61,8 @@ namespace Falcor Texture1D, Texture2D, Texture3D, - TextureCube + TextureCube, + RawBuffer, }; Field(const std::string& name, const std::string& desc, Visibility v); @@ -69,17 +70,23 @@ namespace Falcor bool isValid() const; - Field& texture1D(uint32_t width = 0); - Field& texture2D(uint32_t width = 0, uint32_t height = 0, uint32_t sampleCount = 1); - Field& texture3D(uint32_t width = 0, uint32_t height = 0, uint32_t depth = 0); - Field& textureCube(uint32_t width = 0, uint32_t height = 0); + /** If the `mipLevel` argument to any of the `texture*` functions is set to `kMaxMipLevels`, it will generate the entire mip-chain based on the texture dimensions + */ + static const uint32_t kMaxMipLevels = Texture::kMaxPossible; + + Field& rawBuffer(uint32_t size); + Field& texture1D(uint32_t width = 0, uint32_t mipCount = 1, uint32_t arraySize = 1); + Field& texture2D(uint32_t width = 0, uint32_t height = 0, uint32_t sampleCount = 1, uint32_t mipCount = 1, uint32_t arraySize = 1); + Field& texture3D(uint32_t width = 0, uint32_t height = 0, uint32_t depth = 0, uint32_t arraySize = 1); + Field& textureCube(uint32_t width = 0, uint32_t height = 0, uint32_t mipCount = 1, uint32_t arraySize = 1); + Field& resourceType(Type type, uint32_t width, uint32_t height, uint32_t depth, uint32_t sampleCount, uint32_t mipCount, uint32_t arraySize); - Field& arraySize(uint32_t a); - Field& mipLevels(uint32_t m); Field& format(ResourceFormat f); - Field& bindFlags(Resource::BindFlags flags); + Field& bindFlags(ResourceBindFlags flags); Field& flags(Flags flags); Field& visibility(Visibility vis); + Field& name(const std::string& name); + Field& desc(const std::string& desc); const std::string& getName() const { return mName; } const std::string& getDesc() const { return mDesc; } @@ -88,13 +95,20 @@ namespace Falcor uint32_t getDepth() const { return mDepth; } uint32_t getSampleCount() const { return mSampleCount; } uint32_t getArraySize() const { return mArraySize; } - uint32_t getMipLevels() const { return mMipLevels; } + uint32_t getMipCount() const { return mMipCount; } ResourceFormat getFormat() const { return mFormat; } - Resource::BindFlags getBindFlags() const { return mBindFlags; } + ResourceBindFlags getBindFlags() const { return mBindFlags; } Flags getFlags() const { return mFlags; } Type getType() const { return mType; } Visibility getVisibility() const { return mVisibility; } + /** Overwrite previously unknown/unspecified fields with specified ones. + If a property is specified both in the current object, as well as the other field, an error will be logged and the current field will be undefined + */ + Field& merge(const Field& other); + + bool operator==(const Field& other) const; + bool operator!=(const Field& other) const { return !(other == *this); } private: friend class RenderPassReflection; @@ -105,10 +119,10 @@ namespace Falcor uint32_t mHeight = 0; ///< 0 means don't care - the pass will use whatever is bound (the RenderGraph will use the window size if this field is 0) uint32_t mDepth = 0; ///< 0 means don't care - the pass will use whatever is bound (the RenderGraph will use the window size if this field is 0) uint32_t mSampleCount = 1; ///< 0 means don't care - the pass will use whatever is bound - uint32_t mMipLevels = 1; ///< The required mip-level count. Only valid for textures + uint32_t mMipCount = 1; ///< The required mip-level count. Only valid for textures uint32_t mArraySize = 1; ///< The required array-size. Only valid for textures ResourceFormat mFormat = ResourceFormat::Unknown; ///< Unknown means use the back-buffer format for output resources, don't care for input resources - Resource::BindFlags mBindFlags = Resource::BindFlags::None; ///< The required bind flags. The default for outputs is RenderTarget, for inputs is ShaderResource and for InOut (RenderTarget | ShaderResource) + ResourceBindFlags mBindFlags = ResourceBindFlags::None; ///< The required bind flags. The default for outputs is RenderTarget, for inputs is ShaderResource and for InOut (RenderTarget | ShaderResource) Flags mFlags = Flags::None; ///< The field flags Visibility mVisibility = Visibility::Undefined; }; @@ -119,8 +133,13 @@ namespace Falcor Field& addInternal(const std::string& name, const std::string& desc); size_t getFieldCount() const { return mFields.size(); } - const Field& getField(size_t f) const { return mFields[f]; } - const Field& getField(const std::string& name) const; + const Field* getField(size_t f) const { return f <= mFields.size() ? &mFields[f] : nullptr; } + const Field* getField(const std::string& name) const; + Field& addField(const Field& field); + + bool operator==(const RenderPassReflection& other) const; + bool operator!=(const RenderPassReflection& other) const { return !(*this == other); } + private: Field& addField(const std::string& name, const std::string& desc, Field::Visibility visibility); std::vector mFields; @@ -134,6 +153,7 @@ namespace Falcor #define t2s(ft) case RenderPassReflection::Field::Type::ft: return #ft; switch (t) { + t2s(RawBuffer); t2s(Texture1D); t2s(Texture2D); t2s(Texture3D); @@ -144,4 +164,22 @@ namespace Falcor } #undef t2s } -} \ No newline at end of file + + inline RenderPassReflection::Field::Type resourceTypeToFieldType(Resource::Type t) + { + switch (t) + { + case Resource::Type::Texture1D: + return RenderPassReflection::Field::Type::Texture1D; + case Resource::Type::Texture2D: + case Resource::Type::Texture2DMultisample: + return RenderPassReflection::Field::Type::Texture2D; + case Resource::Type::Texture3D: + return RenderPassReflection::Field::Type::Texture3D; + case Resource::Type::TextureCube: + return RenderPassReflection::Field::Type::TextureCube; + default: + throw std::runtime_error("resourceTypeToFieldType - No RenderPassReflection::Field::Type exists for Resource::Type::" + to_string(t)); + } + } +} diff --git a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.h b/Source/Falcor/RenderGraph/RenderPassStandardFlags.h similarity index 68% rename from Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.h rename to Source/Falcor/RenderGraph/RenderPassStandardFlags.h index cf9f67dbe..ecf34f021 100644 --- a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.h +++ b/Source/Falcor/RenderGraph/RenderPassStandardFlags.h @@ -26,29 +26,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Falcor.h" -#include "FalcorExperimental.h" -using namespace Falcor; - -class MyBlitPass : public RenderPass, inherit_shared_from_this +namespace Falcor { -public: - using SharedPtr = std::shared_ptr; - static const char* kDesc; + /** Flags to indicate what have changed since last frame. + One or more flags can be OR'ed together. + */ + enum class RenderPassRefreshFlags : uint32_t + { + None = 0x0, + LightingChanged = 0x1, ///< Lighting has changed. + RenderOptionsChanged = 0x2, ///< Options that affect the rendering have changed. + }; - /** Create a new object + /** The refresh flags above are passed to RenderPass::execute() via a + field with this name in the dictionary. */ - static SharedPtr create(const Dictionary& dict = {}); + static const char kRenderPassRefreshFlags[] = "_refreshFlags"; - RenderPassReflection reflect() const override; - void execute(RenderContext* pContext, const RenderData* pRenderData) override; - void renderUI(Gui* pGui, const char* uiGroup) override; - Dictionary getScriptingDictionary() const override; - std::string getDesc() override { return kDesc; } + /** First available preudorandom number generator dimension. + */ + static const char kRenderPassPRNGDimension[] = "_prngDimension"; - void setFilter(Sampler::Filter filter) { mFilter = filter; } -private: - MyBlitPass(); - Sampler::Filter mFilter = Sampler::Filter::Linear; -}; \ No newline at end of file + enum_class_operators(RenderPassRefreshFlags); +} diff --git a/Source/Falcor/RenderGraph/ResourceCache.cpp b/Source/Falcor/RenderGraph/ResourceCache.cpp new file mode 100644 index 000000000..5746a2fd0 --- /dev/null +++ b/Source/Falcor/RenderGraph/ResourceCache.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ResourceCache.h" +#include "Core/API/Texture.h" + +namespace Falcor +{ + ResourceCache::SharedPtr ResourceCache::create() + { + return SharedPtr(new ResourceCache()); + } + + void ResourceCache::reset() + { + mNameToIndex.clear(); + mResourceData.clear(); + } + + const Resource::SharedPtr& ResourceCache::getResource(const std::string& name) const + { + static const Resource::SharedPtr pNull; + auto extIt = mExternalResources.find(name); + + // Search external resources if not found in render graph resources + if (extIt == mExternalResources.end()) + { + const auto& it = mNameToIndex.find(name); + if (it == mNameToIndex.end()) return pNull; + return mResourceData[it->second].pResource; + } + + return extIt->second; + } + + const RenderPassReflection::Field& ResourceCache::getResourceReflection(const std::string& name) const + { + uint32_t i = mNameToIndex.at(name); + return mResourceData[i].field; + } + + void ResourceCache::registerExternalResource(const std::string& name, const Resource::SharedPtr& pResource) + { + if(pResource) mExternalResources[name] = pResource; + else + { + auto it = mExternalResources.find(name); + if (it == mExternalResources.end()) + { + logWarning("ResourceCache::registerExternalResource: " + name + " does not exist."); + return; + } + + mExternalResources.erase(it); + } + } + + void mergeTimePoint(std::pair& range, uint32_t newTime) + { + range.first = min(range.first, newTime); + range.second = max(range.second, newTime); + } + + void ResourceCache::registerField(const std::string& name, const RenderPassReflection::Field& field, uint32_t timePoint, const std::string& alias) + { + assert(mNameToIndex.find(name) == mNameToIndex.end()); + + bool addAlias = (alias.empty() == false); + if (addAlias && mNameToIndex.count(alias) == 0) + { + throw std::exception(("ResourceCache::registerField: Field named " + alias + " not found. Cannot add " + name + " as an alias").c_str()); + } + + // Add a new field + if (addAlias == false) + { + assert(mNameToIndex.count(name) == 0); + mNameToIndex[name] = (uint32_t)mResourceData.size(); + bool resolveBindFlags = (field.getBindFlags() == ResourceBindFlags::None); + mResourceData.push_back({ field, {timePoint, timePoint}, nullptr, resolveBindFlags, name }); + } + else // Add alias + { + uint32_t index = mNameToIndex[alias]; + mNameToIndex[name] = index; + mResourceData[index].field.merge(field); + mergeTimePoint(mResourceData[index].lifetime, timePoint); + mResourceData[index].pResource = nullptr; + mResourceData[index].resolveBindFlags = mResourceData[index].resolveBindFlags || (field.getBindFlags() == ResourceBindFlags::None); + } + } + + Resource::SharedPtr createResourceForPass(const ResourceCache::DefaultProperties& params, const RenderPassReflection::Field& field, bool resolveBindFlags, const std::string& resourceName) + { + uint32_t width = field.getWidth() ? field.getWidth() : params.dims.x; + uint32_t height = field.getHeight() ? field.getHeight() : params.dims.y; + uint32_t depth = field.getDepth() ? field.getDepth() : 1; + uint32_t sampleCount = field.getSampleCount() ? field.getSampleCount() : 1; + auto bindFlags = field.getBindFlags(); + auto arraySize = field.getArraySize(); + auto mipLevels = field.getMipCount(); + + ResourceFormat format = ResourceFormat::Unknown; + + if (field.getType() != RenderPassReflection::Field::Type::RawBuffer) + { + format = field.getFormat() == ResourceFormat::Unknown ? params.format : field.getFormat(); + if (resolveBindFlags) + { + ResourceBindFlags mask = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; + bool isOutput = is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output); + bool isInternal = is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Internal); + if (isOutput || isInternal) mask |= Resource::BindFlags::DepthStencil | Resource::BindFlags::RenderTarget; + auto supported = getFormatBindFlags(format); + mask &= supported; + bindFlags |= mask; + } + } + else // RawBuffer + { + if (resolveBindFlags) bindFlags = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; + } + Resource::SharedPtr pResource; + + switch (field.getType()) + { + case RenderPassReflection::Field::Type::RawBuffer: + pResource = Buffer::create(width, bindFlags, Buffer::CpuAccess::None); + break; + case RenderPassReflection::Field::Type::Texture1D: + pResource = Texture::create1D(width, format, arraySize, mipLevels, nullptr, bindFlags); + break; + case RenderPassReflection::Field::Type::Texture2D: + if (sampleCount > 1) + { + pResource = Texture::create2DMS(width, height, format, sampleCount, arraySize, bindFlags); + } + else + { + pResource = Texture::create2D(width, height, format, arraySize, mipLevels, nullptr, bindFlags); + } + break; + case RenderPassReflection::Field::Type::Texture3D: + pResource = Texture::create3D(width, height, depth, format, mipLevels, nullptr, bindFlags); + break; + case RenderPassReflection::Field::Type::TextureCube: + pResource = Texture::createCube(width, height, format, arraySize, mipLevels, nullptr, bindFlags); + break; + default: + should_not_get_here(); + return nullptr; + } + pResource->setName(resourceName); + return pResource; + } + + void ResourceCache::allocateResources(const DefaultProperties& params) + { + for (auto& data : mResourceData) + { + if ((data.pResource == nullptr) && (data.field.isValid())) + { + data.pResource = createResourceForPass(params, data.field, data.resolveBindFlags, data.name); + } + } + } +} diff --git a/Framework/Source/Experimental/RenderGraph/ResourceCache.h b/Source/Falcor/RenderGraph/ResourceCache.h similarity index 70% rename from Framework/Source/Experimental/RenderGraph/ResourceCache.h rename to Source/Falcor/RenderGraph/ResourceCache.h index eb8194b11..e4ada4e6e 100644 --- a/Framework/Source/Experimental/RenderGraph/ResourceCache.h +++ b/Source/Falcor/RenderGraph/ResourceCache.h @@ -26,33 +26,35 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPassReflection.h" -#include "API/Texture.h" +#include "RenderGraph/RenderPassReflection.h" +#include "Core/API/Resource.h" namespace Falcor -{ - class RenderPass; - class Resource; - - class ResourceCache : public std::enable_shared_from_this +{ + class dlldecl ResourceCache : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; + using ResourcesMap = std::unordered_map; + + /** Create a new object + */ static SharedPtr create(); /** Properties to use during resource creation when its property has not been fully specified. */ struct DefaultProperties { - uint32_t width = 0; ///< Width to create textures as - uint32_t height = 0; ///< Height to create textures as + uvec2 dims; ///< Width, height of the swap chain ResourceFormat format = ResourceFormat::Unknown; ///< Format to use for texture creation }; - // Add/Remove reference to a graph input resource not owned by the cache - void registerExternalInput(const std::string& name, const std::shared_ptr& pResource); - void removeExternalInput(const std::string& name); - + /** Add/Remove reference to a graph input resource not owned by the cache + \param[in] name The resource's name + \param[in] pResource The resource to register. If this is null, will unregister the resource + */ + void registerExternalResource(const std::string& name, const Resource::SharedPtr& pResource); + /** Register a field that requires resources to be allocated. \param[in] name String in the format of PassName.FieldName \param[in] field Reflection data for the field @@ -60,11 +62,11 @@ namespace Falcor \param[in] alias Optional. Another field name described in the same way as 'name'. If specified, and the field exists in the cache, the resource will be aliased with 'name' and field properties will be merged. */ - void registerField(const std::string& name, RenderPassReflection::Field field, uint32_t timePoint, const std::string& alias = ""); + void registerField(const std::string& name, const RenderPassReflection::Field& field, uint32_t timePoint, const std::string& alias = ""); /** Get a resource by name. Includes external resources known by the cache. */ - const std::shared_ptr& getResource(const std::string& name) const; + const Resource::SharedPtr& getResource(const std::string& name) const; /** Get the field-reflection of a resource */ @@ -84,14 +86,11 @@ namespace Falcor struct ResourceData { - RenderPassReflection::Field field; // Holds merged properties for aliased resources - bool dirty = true; // Whether field data has been changed since last resource creation - - // Time range where this resource is being used - uint32_t firstUsed = 0; - uint32_t lastUsed = 0; - - std::shared_ptr pResource; + RenderPassReflection::Field field; // Holds merged properties for aliased resources + std::pair lifetime; // Time range where this resource is being used + Resource::SharedPtr pResource; // The resource + bool resolveBindFlags; // Whether or not we should resolve the field's bind-flags before creating the resource + std::string name; // Full name of the resource, including the pass name }; // Resources and properties for fields within (and therefore owned by) a render graph @@ -99,7 +98,7 @@ namespace Falcor std::vector mResourceData; // References to output resources not to be allocated by the render graph - std::unordered_map> mExternalInputs; + ResourcesMap mExternalResources; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/BlitPass.cpp b/Source/Falcor/RenderPasses/BlitPass.cpp similarity index 76% rename from Framework/Source/Experimental/RenderPasses/BlitPass.cpp rename to Source/Falcor/RenderPasses/BlitPass.cpp index 69578327c..857b0c1a9 100644 --- a/Framework/Source/Experimental/RenderPasses/BlitPass.cpp +++ b/Source/Falcor/RenderPasses/BlitPass.cpp @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "BlitPass.h" -#include "API/RenderContext.h" -#include "Utils/Gui.h" namespace Falcor { @@ -38,7 +36,7 @@ namespace Falcor static const std::string kSrc = "src"; static const std::string kFilter = "filter"; - RenderPassReflection BlitPass::reflect() const + RenderPassReflection BlitPass::reflect(const CompileData& compileData) { RenderPassReflection reflector; @@ -65,27 +63,23 @@ namespace Falcor return true; } - BlitPass::SharedPtr BlitPass::create(const Dictionary& dict) + BlitPass::SharedPtr BlitPass::create(RenderContext* pRenderContext, const Dictionary& dict) { SharedPtr pPass = SharedPtr(new BlitPass); return parseDictionary(pPass.get(), dict) ? pPass : nullptr; } - Dictionary BlitPass::getScriptingDictionary() const + Dictionary BlitPass::getScriptingDictionary() { Dictionary dict; dict[kFilter] = mFilter; return dict; } - BlitPass::BlitPass() : RenderPass("BlitPass") + void BlitPass::execute(RenderContext* pContext, const RenderData& renderData) { - } - - void BlitPass::execute(RenderContext* pContext, const RenderData* pRenderData) - { - const auto& pSrcTex = pRenderData->getTexture(kSrc); - const auto& pDstTex = pRenderData->getTexture(kDst); + const auto& pSrcTex = renderData[kSrc]->asTexture(); + const auto& pDstTex = renderData[kDst]->asTexture(); if(pSrcTex && pDstTex) { @@ -97,20 +91,15 @@ namespace Falcor } } - void BlitPass::renderUI(Gui* pGui, const char* uiGroup) + void BlitPass::renderUI(Gui::Widgets& widget) { - if (!uiGroup || pGui->beginGroup(uiGroup)) + static const Gui::DropdownList kFilterList = { - static const Gui::DropdownList kFilterList = - { - { (uint32_t)Sampler::Filter::Linear, "Linear" }, - { (uint32_t)Sampler::Filter::Point, "Point" }, - }; - - uint32_t f = (uint32_t)mFilter; - if (pGui->addDropdown("Filter", kFilterList, f)) setFilter((Sampler::Filter)f); + { (uint32_t)Sampler::Filter::Linear, "Linear" }, + { (uint32_t)Sampler::Filter::Point, "Point" }, + }; + uint32_t f = (uint32_t)mFilter; - if (uiGroup) pGui->endGroup(); - } + if (widget.dropdown("Filter", kFilterList, f)) setFilter((Sampler::Filter)f); } -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/BlitPass.h b/Source/Falcor/RenderPasses/BlitPass.h similarity index 77% rename from Framework/Source/Experimental/RenderPasses/BlitPass.h rename to Source/Falcor/RenderPasses/BlitPass.h index fe4c444d4..b88e5c84a 100644 --- a/Framework/Source/Experimental/RenderPasses/BlitPass.h +++ b/Source/Falcor/RenderPasses/BlitPass.h @@ -26,30 +26,32 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "API/Sampler.h" +#include "RenderGraph/RenderPass.h" +#include "Core/API/Sampler.h" namespace Falcor { - class BlitPass : public RenderPass, inherit_shared_from_this + class dlldecl BlitPass : public RenderPass, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; + static const char* kDesc; /** Create a new object */ - static SharedPtr create(const Dictionary& dict = {}); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - virtual RenderPassReflection reflect() const override; - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; - virtual void renderUI(Gui* pGui, const char* uiGroup) override; - virtual Dictionary getScriptingDictionary() const override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual Dictionary getScriptingDictionary() override; virtual std::string getDesc() override { return kDesc; } void setFilter(Sampler::Filter filter) { mFilter = filter; } private: - BlitPass(); + BlitPass() = default; Sampler::Filter mFilter = Sampler::Filter::Linear; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/DepthPass.cpp b/Source/Falcor/RenderPasses/DepthPass.cpp similarity index 76% rename from Framework/Source/Experimental/RenderPasses/DepthPass.cpp rename to Source/Falcor/RenderPasses/DepthPass.cpp index 3140e56ed..d443fd701 100644 --- a/Framework/Source/Experimental/RenderPasses/DepthPass.cpp +++ b/Source/Falcor/RenderPasses/DepthPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "DepthPass.h" namespace Falcor @@ -51,58 +51,52 @@ namespace Falcor return true; } - Dictionary DepthPass::getScriptingDictionary() const + Dictionary DepthPass::getScriptingDictionary() { Dictionary d; d[kDepthFormat] = mDepthFormat; return d; } - DepthPass::SharedPtr DepthPass::create(const Dictionary& dict) + DepthPass::SharedPtr DepthPass::create(RenderContext* pRenderContext, const Dictionary& dict) { DepthPass* pThis = new DepthPass; return parseDictionary(pThis, dict) ? SharedPtr(pThis) : nullptr; } - DepthPass::DepthPass() : RenderPass("DepthPass") + DepthPass::DepthPass() { - GraphicsProgram::SharedPtr pProgram = GraphicsProgram::create({}); + GraphicsProgram::SharedPtr pProgram = GraphicsProgram::createFromFile("Data/RenderPasses/DepthPass.ps.slang", "", "main"); mpState = GraphicsState::create(); mpState->setProgram(pProgram); - mpVars = GraphicsVars::create(pProgram->getReflector()); mpFbo = Fbo::create(); } - RenderPassReflection DepthPass::reflect() const + RenderPassReflection DepthPass::reflect(const CompileData& compileData) { RenderPassReflection reflector; reflector.addOutput(kDepth, "Depth-buffer").bindFlags(Resource::BindFlags::DepthStencil).format(mDepthFormat).texture2D(0, 0, 0); return reflector; } - void DepthPass::setScene(const Scene::SharedPtr& pScene) + void DepthPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) { - mpSceneRenderer = nullptr; - if (pScene) - { - mpSceneRenderer = SceneRenderer::create(pScene); - } + mpScene = pScene; + mpState->getProgram()->addDefines(mpScene->getSceneDefines()); + mpVars = GraphicsVars::create(mpState->getProgram()->getReflector()); } - void DepthPass::execute(RenderContext* pContext, const RenderData* pData) + void DepthPass::execute(RenderContext* pContext, const RenderData& renderData) { - if(mpSceneRenderer) + if(mpScene) { - const auto& pDepth = pData->getTexture(kDepth); + const auto& pDepth = renderData[kDepth]->asTexture(); mpFbo->attachDepthStencilTarget(pDepth); mpState->setFbo(mpFbo); - pContext->pushGraphicsState(mpState); - pContext->pushGraphicsVars(mpVars); pContext->clearDsv(pDepth->getDSV().get(), 1, 0); - mpSceneRenderer->renderScene(pContext); - pContext->popGraphicsState(); - pContext->popGraphicsVars(); + pContext->clearFbo(mpFbo.get(), vec4(0, 0, 0, 0), 1, 0); + mpScene->render(pContext, mpState.get(), mpVars.get()); } } @@ -134,14 +128,10 @@ namespace Falcor { (uint32_t)ResourceFormat::D32FloatS8X24, "D32FloatS8X24" }, }; - void DepthPass::renderUI(Gui* pGui, const char* uiGroup) + void DepthPass::renderUI(Gui::Widgets& widget) { - if (!uiGroup || pGui->beginGroup(uiGroup)) - { - uint32_t depthFormat = (uint32_t)mDepthFormat; - if (pGui->addDropdown("Buffer Format", kDepthFormats, depthFormat)) setDepthBufferFormat(ResourceFormat(depthFormat)); + uint32_t depthFormat = (uint32_t)mDepthFormat; - if (uiGroup) pGui->endGroup(); - } + if (widget.dropdown("Buffer Format", kDepthFormats, depthFormat)) setDepthBufferFormat(ResourceFormat(depthFormat)); } -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/DepthPass.h b/Source/Falcor/RenderPasses/DepthPass.h similarity index 74% rename from Framework/Source/Experimental/RenderPasses/DepthPass.h rename to Source/Falcor/RenderPasses/DepthPass.h index 550a70401..dbe123203 100644 --- a/Framework/Source/Experimental/RenderPasses/DepthPass.h +++ b/Source/Falcor/RenderPasses/DepthPass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,29 +26,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "Graphics/Program/GraphicsProgram.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/GraphicsState.h" -#include "Graphics/Scene/SceneRenderer.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" namespace Falcor { - class DepthPass : public RenderPass, inherit_shared_from_this + class dlldecl DepthPass : public RenderPass, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; static const char* kDesc; /** Create a new object */ - static SharedPtr create(const Dictionary& dict = {}); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - virtual RenderPassReflection reflect() const override; - virtual void execute(RenderContext* pContext, const RenderData* pData) override; - virtual void setScene(const Scene::SharedPtr& pScene); - virtual void renderUI(Gui* pGui, const char* uiGroup) override; - virtual Dictionary getScriptingDictionary() const override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual Dictionary getScriptingDictionary() override; virtual std::string getDesc() { return kDesc; } DepthPass& setDepthBufferFormat(ResourceFormat format); @@ -58,7 +56,7 @@ namespace Falcor Fbo::SharedPtr mpFbo; GraphicsState::SharedPtr mpState; GraphicsVars::SharedPtr mpVars; - SceneRenderer::SharedPtr mpSceneRenderer; ResourceFormat mDepthFormat = ResourceFormat::D32Float; + Scene::SharedPtr mpScene; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/ForwardLightingPass.cpp b/Source/Falcor/RenderPasses/ForwardLightingPass.cpp similarity index 75% rename from Framework/Source/Experimental/RenderPasses/ForwardLightingPass.cpp rename to Source/Falcor/RenderPasses/ForwardLightingPass.cpp index ef5f043d8..ce9256fbe 100644 --- a/Framework/Source/Experimental/RenderPasses/ForwardLightingPass.cpp +++ b/Source/Falcor/RenderPasses/ForwardLightingPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ForwardLightingPass.h" namespace Falcor @@ -33,16 +33,19 @@ namespace Falcor const char* ForwardLightingPass::kDesc = "The pass computes the lighting results for the current scene. It will compute direct-illumination, indirect illumination from the light-probe and apply shadows (if a visibility map is provided).\n" "The pass can output the world-space normals and screen-space motion vectors, both are optional"; - static std::string kDepth = "depth"; - static std::string kColor = "color"; - static std::string kMotionVecs = "motionVecs"; - static std::string kNormals = "normals"; - static std::string kVisBuffer = "visibilityBuffer"; + namespace + { + const std::string kDepth = "depth"; + const std::string kColor = "color"; + const std::string kMotionVecs = "motionVecs"; + const std::string kNormals = "normals"; + const std::string kVisBuffer = "visibilityBuffer"; - static std::string kSampleCount = "sampleCount"; - static std::string kSuperSampling = "enableSuperSampling"; + const std::string kSampleCount = "sampleCount"; + const std::string kSuperSampling = "enableSuperSampling"; + } - ForwardLightingPass::SharedPtr ForwardLightingPass::create(const Dictionary& dict) + ForwardLightingPass::SharedPtr ForwardLightingPass::create(RenderContext* pRenderContext, const Dictionary& dict) { auto pThis = SharedPtr(new ForwardLightingPass()); pThis->setColorFormat(ResourceFormat::RGBA32Float).setMotionVecFormat(ResourceFormat::RG16Float).setNormalMapFormat(ResourceFormat::RGBA8Unorm).setSampleCount(1).usePreGeneratedDepthBuffer(true); @@ -51,7 +54,7 @@ namespace Falcor { if (v.key() == kSampleCount) pThis->setSampleCount(v.val()); else if (v.key() == kSuperSampling) pThis->setSuperSampling(v.val()); - logWarning("Unknown field `" + v.key() + "` in a ForwardLightingPass dictionary"); + else logWarning("Unknown field `" + v.key() + "` in a ForwardLightingPass dictionary"); } return pThis; @@ -65,24 +68,20 @@ namespace Falcor return d; } - ForwardLightingPass::ForwardLightingPass() : RenderPass("ForwardLightingPass") + ForwardLightingPass::ForwardLightingPass() { GraphicsProgram::SharedPtr pProgram = GraphicsProgram::createFromFile("RenderPasses/ForwardLightingPass.slang", "", "ps"); mpState = GraphicsState::create(); mpState->setProgram(pProgram); - mpVars = GraphicsVars::create(pProgram->getReflector()); - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - setSampler(Sampler::create(samplerDesc)); mpFbo = Fbo::create(); DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(true).setDepthWriteMask(false).setStencilTest(false).setDepthFunc(DepthStencilState::Func::LessEqual); + dsDesc.setDepthWriteMask(false).setDepthFunc(DepthStencilState::Func::LessEqual); mpDsNoDepthWrite = DepthStencilState::create(dsDesc); } - RenderPassReflection ForwardLightingPass::reflect() const + RenderPassReflection ForwardLightingPass::reflect(const CompileData& compileData) { RenderPassReflection reflector; @@ -105,15 +104,19 @@ namespace Falcor return reflector; } - void ForwardLightingPass::setScene(const Scene::SharedPtr& pScene) + void ForwardLightingPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) { - mpSceneRenderer = nullptr; - if (pScene) mpSceneRenderer = SceneRenderer::create(pScene); + mpScene = pScene; + mpState->getProgram()->addDefines(pScene->getSceneDefines()); + mpVars = GraphicsVars::create(mpState->getProgram()->getReflector()); + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + setSampler(Sampler::create(samplerDesc)); } - void ForwardLightingPass::initDepth(const RenderData* pRenderData) + void ForwardLightingPass::initDepth(const RenderData& renderData) { - const auto& pTexture = pRenderData->getTexture(kDepth); + const auto& pTexture = renderData[kDepth]->asTexture(); if (pTexture) { @@ -131,11 +134,11 @@ namespace Falcor } } - void ForwardLightingPass::initFbo(RenderContext* pContext, const RenderData* pRenderData) + void ForwardLightingPass::initFbo(RenderContext* pContext, const RenderData& renderData) { - mpFbo->attachColorTarget(pRenderData->getTexture(kColor), 0); - mpFbo->attachColorTarget(pRenderData->getTexture(kNormals), 1); - mpFbo->attachColorTarget(pRenderData->getTexture(kMotionVecs), 2); + mpFbo->attachColorTarget(renderData[kColor]->asTexture(), 0); + mpFbo->attachColorTarget(renderData[kNormals]->asTexture(), 1); + mpFbo->attachColorTarget(renderData[kMotionVecs]->asTexture(), 2); for(uint32_t i = 1 ; i < 3 ; i++) { @@ -144,29 +147,25 @@ namespace Falcor } // TODO Matt (not really matt, just need to fix that since if depth is not bound the pass crashes - if (mUsePreGenDepth == false) pContext->clearDsv(pRenderData->getTexture(kDepth)->getDSV().get(), 1, 0); + if (mUsePreGenDepth == false) pContext->clearDsv(renderData[kDepth]->asTexture()->getDSV().get(), 1, 0); } - void ForwardLightingPass::execute(RenderContext* pContext, const RenderData* pRenderData) + void ForwardLightingPass::execute(RenderContext* pContext, const RenderData& renderData) { - initDepth(pRenderData); - initFbo(pContext, pRenderData); + initDepth(renderData); + initFbo(pContext, renderData); - if (mpSceneRenderer) + if (mpScene) { mpVars["PerFrameCB"]["gRenderTargetDim"] = vec2(mpFbo->getWidth(), mpFbo->getHeight()); - mpVars->setTexture(kVisBuffer, pRenderData->getTexture(kVisBuffer)); + mpVars->setTexture(kVisBuffer, renderData[kVisBuffer]->asTexture()); mpState->setFbo(mpFbo); - pContext->pushGraphicsState(mpState); - pContext->pushGraphicsVars(mpVars); - mpSceneRenderer->renderScene(pContext); - pContext->popGraphicsState(); - pContext->popGraphicsVars(); + mpScene->render(pContext, mpState.get(), mpVars.get()); } } - void ForwardLightingPass::renderUI(Gui* pGui, const char* uiGroup) + void ForwardLightingPass::renderUI(Gui::Widgets& widget) { static const Gui::DropdownList kSampleCountList = { @@ -176,13 +175,8 @@ namespace Falcor { 8, "8" }, }; - if(!uiGroup || pGui->beginGroup(uiGroup)) - { - if (pGui->addDropdown("Sample Count", kSampleCountList, mSampleCount)) setSampleCount(mSampleCount); - if (mSampleCount > 1 && pGui->addCheckBox("Super Sampling", mEnableSuperSampling)) setSuperSampling(mEnableSuperSampling); - - if (uiGroup) pGui->endGroup(); - } + if (widget.dropdown("Sample Count", kSampleCountList, mSampleCount)) setSampleCount(mSampleCount); + if (mSampleCount > 1 && widget.checkbox("Super Sampling", mEnableSuperSampling)) setSuperSampling(mEnableSuperSampling); } ForwardLightingPass& ForwardLightingPass::setColorFormat(ResourceFormat format) @@ -250,4 +244,4 @@ namespace Falcor mpVars->setSampler("gSampler", pSampler); return *this; } -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/ForwardLightingPass.h b/Source/Falcor/RenderPasses/ForwardLightingPass.h similarity index 78% rename from Framework/Source/Experimental/RenderPasses/ForwardLightingPass.h rename to Source/Falcor/RenderPasses/ForwardLightingPass.h index 301db8c0d..c3fac2c56 100644 --- a/Framework/Source/Experimental/RenderPasses/ForwardLightingPass.h +++ b/Source/Falcor/RenderPasses/ForwardLightingPass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,29 +26,28 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "Graphics/Program/GraphicsProgram.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/GraphicsState.h" -#include "Graphics/Scene/SceneRenderer.h" +#include "RenderGraph/RenderPass.h" +#include "Core/Program/ProgramVars.h" +#include "Scene/Scene.h" namespace Falcor { - class ForwardLightingPass : public RenderPass, inherit_shared_from_this + class dlldecl ForwardLightingPass : public RenderPass, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; static const char* kDesc; /** Create a new object */ - static SharedPtr create(const Dictionary& dict = {}); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - virtual RenderPassReflection reflect() const override; - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; - virtual void setScene(const std::shared_ptr& pScene) override; - virtual void renderUI(Gui* pGui, const char* uiGroup) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void renderUI(Gui::Widgets& widget) override; virtual Dictionary getScriptingDictionary() const; /** Set the color target format. This is always enabled @@ -77,21 +76,21 @@ namespace Falcor /** Set a sampler-state to be used during rendering. The default is tri-linear */ - ForwardLightingPass& setSampler(const std::shared_ptr& pSampler); + ForwardLightingPass& setSampler(const Sampler::SharedPtr& pSampler); /** Get a description of the pass */ std::string getDesc() override { return kDesc; } private: ForwardLightingPass(); - void initDepth(const RenderData* pRenderData); - void initFbo(RenderContext* pContext, const RenderData* pRenderData); + void initDepth(const RenderData& renderData); + void initFbo(RenderContext* pContext, const RenderData& renderData); Fbo::SharedPtr mpFbo; GraphicsState::SharedPtr mpState; DepthStencilState::SharedPtr mpDsNoDepthWrite; + Scene::SharedPtr mpScene; GraphicsVars::SharedPtr mpVars; - SceneRenderer::SharedPtr mpSceneRenderer; ResourceFormat mColorFormat = ResourceFormat::Unknown; ResourceFormat mNormalMapFormat = ResourceFormat::Unknown; @@ -100,4 +99,4 @@ namespace Falcor bool mEnableSuperSampling = false; bool mUsePreGenDepth = false; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/RenderPasses/ImageLoader.cpp b/Source/Falcor/RenderPasses/ImageLoader.cpp new file mode 100644 index 000000000..e1e929a43 --- /dev/null +++ b/Source/Falcor/RenderPasses/ImageLoader.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ImageLoader.h" + +namespace Falcor +{ + const char* ImageLoader::kDesc = "Load an image into a texture"; + + namespace + { + const std::string kDst = "dst"; + const std::string kImage = "filename"; + const std::string kMips = "mips"; + const std::string kSrgb = "srgb"; + const std::string kArraySlice = "arrayIndex"; + const std::string kMipLevel = "mipLevel"; + } + + RenderPassReflection ImageLoader::reflect(const CompileData& compileData) + { + RenderPassReflection reflector; + reflector.addOutput(kDst, "Destination texture"); + return reflector; + } + + ImageLoader::SharedPtr ImageLoader::create(RenderContext* pRenderContext, const Dictionary& dict) + { + SharedPtr pPass = SharedPtr(new ImageLoader); + + for (const auto& v : dict) + { + if (v.key() == kImage) pPass->mImageName = v.val().operator std::string(); + else if(v.key() == kSrgb) pPass->mLoadSRGB = v.val(); + else if (v.key() == kMips) pPass->mGenerateMips = v.val(); + else if (v.key() == kArraySlice) pPass->mArraySlice = v.val(); + else if (v.key() == kMipLevel) pPass->mMipLevel = v.val(); + else logWarning("Unknown field `" + v.key() + "` in a ImageLoader dictionary"); + } + + if (pPass->mImageName.size()) + { + pPass->mpTex = Texture::createFromFile(pPass->mImageName, pPass->mGenerateMips, pPass->mLoadSRGB); + } + + return pPass; + } + + Dictionary ImageLoader::getScriptingDictionary() + { + Dictionary dict; + dict[kImage] = mImageName; + dict[kMips] = mGenerateMips; + dict[kSrgb] = mLoadSRGB; + dict[kArraySlice] = mArraySlice; + dict[kMipLevel] = mMipLevel; + return dict; + } + + ImageLoader::ImageLoader() + { + } + + void ImageLoader::compile(RenderContext* pContext, const CompileData& compileData) + { + if (!mpTex) throw std::runtime_error("ImageLoader::compile - No image loaded!"); + } + + void ImageLoader::execute(RenderContext* pContext, const RenderData& renderData) + { + const auto& pDstTex = renderData[kDst]->asTexture(); + if (!mpTex) + { + pContext->clearRtv(pDstTex->getRTV().get(), glm::vec4(0, 0, 0, 0)); + return; + } + pContext->blit(mpTex->getSRV(mMipLevel, 1, mArraySlice, 1), pDstTex->getRTV()); + } + + void ImageLoader::renderUI(Gui::Widgets& widget) + { + bool reloadImage = widget.textbox("Image File", mImageName); + reloadImage |= widget.checkbox("Load As SRGB", mLoadSRGB); + reloadImage |= widget.checkbox("Generate Mipmaps", mGenerateMips); + if (mGenerateMips) + { + reloadImage |= widget.slider("Mip Level", mMipLevel, 0u, mpTex ? mpTex->getMipCount() : 0u); + } + reloadImage |= widget.slider("Array Slice", mArraySlice, 0u, mpTex ? mpTex->getArraySize() : 0u); + + if (widget.button("Load File")) { reloadImage |= openFileDialog({}, mImageName); } + + if (mpTex) + { + widget.image(mImageName.c_str(), mpTex, { 320, 320 }); + } + + if (reloadImage && mImageName.size()) + { + mImageName = stripDataDirectories(mImageName); + mpTex = Texture::createFromFile(mImageName, mGenerateMips, mLoadSRGB); + } + } +} diff --git a/Framework/Source/Experimental/RenderPasses/ImageLoader.h b/Source/Falcor/RenderPasses/ImageLoader.h similarity index 73% rename from Framework/Source/Experimental/RenderPasses/ImageLoader.h rename to Source/Falcor/RenderPasses/ImageLoader.h index 0ca9667f9..77da5ba74 100644 --- a/Framework/Source/Experimental/RenderPasses/ImageLoader.h +++ b/Source/Falcor/RenderPasses/ImageLoader.h @@ -26,32 +26,35 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "API/Texture.h" +#include "RenderGraph/RenderPass.h" namespace Falcor { - class ImageLoader : public RenderPass, inherit_shared_from_this + class dlldecl ImageLoader : public RenderPass, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; static const char* kDesc; /** Create a new object */ - static SharedPtr create(const Dictionary& dict = {}); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - virtual RenderPassReflection reflect() const override; - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; - virtual void renderUI(Gui* pGui, const char* uiGroup) override; - virtual Dictionary getScriptingDictionary() const override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual Dictionary getScriptingDictionary() override; virtual std::string getDesc() override { return kDesc; } private: ImageLoader(); Texture::SharedPtr mpTex; std::string mImageName; - bool mGenerateMips = true; - bool mLoadSRGB = false; + uint32_t mArraySlice = 0; + uint32_t mMipLevel = 0; + bool mGenerateMips = false; + bool mLoadSRGB = true; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/ResolvePass.cpp b/Source/Falcor/RenderPasses/ResolvePass.cpp similarity index 87% rename from Framework/Source/Experimental/RenderPasses/ResolvePass.cpp rename to Source/Falcor/RenderPasses/ResolvePass.cpp index d21f89e49..d05e1d60d 100644 --- a/Framework/Source/Experimental/RenderPasses/ResolvePass.cpp +++ b/Source/Falcor/RenderPasses/ResolvePass.cpp @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ResolvePass.h" -#include "API/RenderContext.h" -#include "Utils/Gui.h" namespace Falcor { @@ -37,7 +35,7 @@ namespace Falcor static const std::string kDst = "dst"; static const std::string kSrc = "src"; - RenderPassReflection ResolvePass::reflect() const + RenderPassReflection ResolvePass::reflect(const CompileData& compileData) { RenderPassReflection reflector; reflector.addInput(kSrc, "Multi-sampled texture").format(mFormat).texture2D(0, 0, 0); @@ -45,7 +43,7 @@ namespace Falcor return reflector; } - ResolvePass::SharedPtr ResolvePass::create(const Dictionary& dictionary) + ResolvePass::SharedPtr ResolvePass::create(RenderContext* pRenderContext, const Dictionary& dictionary) { try { @@ -57,12 +55,10 @@ namespace Falcor } } - ResolvePass::ResolvePass() : RenderPass("ResolvePass") {} - - void ResolvePass::execute(RenderContext* pContext, const RenderData* pRenderData) + void ResolvePass::execute(RenderContext* pContext, const RenderData& renderData) { - auto pSrcTex = pRenderData->getTexture(kSrc); - auto pDstTex = pRenderData->getTexture(kDst); + auto pSrcTex = renderData[kSrc]->asTexture(); + auto pDstTex = renderData[kDst]->asTexture(); if (pSrcTex && pDstTex) { @@ -79,4 +75,4 @@ namespace Falcor logWarning("ResolvePass::execute() - missing an input or output resource"); } } -} \ No newline at end of file +} diff --git a/Framework/Source/Experimental/RenderPasses/ResolvePass.h b/Source/Falcor/RenderPasses/ResolvePass.h similarity index 81% rename from Framework/Source/Experimental/RenderPasses/ResolvePass.h rename to Source/Falcor/RenderPasses/ResolvePass.h index dad7762fd..eb0469f85 100644 --- a/Framework/Source/Experimental/RenderPasses/ResolvePass.h +++ b/Source/Falcor/RenderPasses/ResolvePass.h @@ -26,27 +26,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Experimental/RenderGraph/RenderPass.h" -#include "API/Texture.h" +#include "RenderGraph/RenderPass.h" namespace Falcor { - class ResolvePass : public RenderPass, inherit_shared_from_this + class dlldecl ResolvePass : public RenderPass, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; static const char* kDesc; /** Create a new object */ - static SharedPtr create(const Dictionary& dictionary = {}); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dictionary = {}); void setFormat(ResourceFormat format) { mFormat = format; } - virtual RenderPassReflection reflect() const override; - virtual void execute(RenderContext* pContext, const RenderData* pRenderData) override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; virtual std::string getDesc() override { return kDesc; } private: - ResolvePass(); + ResolvePass() = default; ResourceFormat mFormat; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Scene/Animation/Animation.cpp b/Source/Falcor/Scene/Animation/Animation.cpp new file mode 100644 index 000000000..9f11a8d8b --- /dev/null +++ b/Source/Falcor/Scene/Animation/Animation.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Animation.h" +#include "glm/gtc/quaternion.hpp" +#include "glm/gtx/transform.hpp" +#include "AnimationController.h" + +namespace Falcor +{ + Animation::SharedPtr Animation::create(const std::string& name, double durationInSeconds) + { + return SharedPtr(new Animation(name, durationInSeconds)); + } + + Animation::Animation(const std::string& name, double durationInSeconds) : mName(name), mDurationInSeconds(durationInSeconds) {} + + size_t Animation::findChannelFrame(const Channel& c, double time) const + { + size_t frameID = (time < c.lastUpdateTime) ? 0 : c.lastKeyframeUsed; + while (frameID < c.keyframes.size() - 1) + { + if (c.keyframes[frameID + 1].time > time) break; + frameID++; + } + return frameID; + } + + mat4 Animation::interpolate(const Keyframe& start, const Keyframe& end, double curTime) const + { + double localTime = curTime - start.time; + double keyframeDuration = end.time - start.time; + if (keyframeDuration < 0) keyframeDuration += mDurationInSeconds; + float factor = keyframeDuration != 0 ? (float)(localTime / keyframeDuration) : 1; + + vec3 translation = lerp(start.translation, end.translation, factor); + vec3 scaling = lerp(start.scaling, end.scaling, factor); + quat rotation = slerp(start.rotation, end.rotation, factor); + + mat4 T; + T[3] = vec4(translation, 1); + mat4 R = mat4_cast(rotation); + mat4 S = scale(scaling); + mat4 transform = T * R * S; + return transform; + } + + mat4 Animation::animateChannel(Channel& c, double time) + { + size_t curKeyIndex = findChannelFrame(c, time); + size_t nextKeyIndex = curKeyIndex + 1; + if (nextKeyIndex == c.keyframes.size()) nextKeyIndex = 0; + + c.lastUpdateTime = time; + c.lastKeyframeUsed = curKeyIndex; + + return interpolate(c.keyframes[curKeyIndex], c.keyframes[nextKeyIndex], time); + } + + void Animation::animate(double totalTime, std::vector& matrices) + { + // Calculate the relative time + double modTime = fmod(totalTime, mDurationInSeconds); + for (auto& c : mChannels) + { + matrices[c.matrixID] = animateChannel(c, modTime); + } + } + + size_t Animation::addChannel(size_t matrixID) + { + mChannels.push_back(Channel(matrixID)); + return mChannels.size() - 1; + } + + void Animation::addKeyframe(size_t channelID, const Keyframe& keyframe) + { + assert(channelID < mChannels.size()); + assert(keyframe.time <= mDurationInSeconds); + + mChannels[channelID].lastKeyframeUsed = 0; + auto& channelFrames = mChannels[channelID].keyframes; + + if (channelFrames.size() == 0 || channelFrames[0].time > keyframe.time) + { + channelFrames.insert(channelFrames.begin(), keyframe); + return; + } + else + { + for (size_t i = 0; i < channelFrames.size(); i++) + { + auto& current = channelFrames[i]; + // If we already have a key-frame at the same time, replace it + if (current.time == keyframe.time) + { + current = keyframe; + return; + } + + // If this is not the last frame, Check if we are in between frames + if (i < channelFrames.size() - 1) + { + auto& Next = channelFrames[i + 1]; + if (current.time < keyframe.time && Next.time > keyframe.time) + { + channelFrames.insert(channelFrames.begin() + i + 1, keyframe); + return; + } + } + } + + // If we got here, need to push it to the end of the list + channelFrames.push_back(keyframe); + } + } + + const Animation::Keyframe& Animation::getKeyframe(size_t channelID, double time) const + { + assert(channelID < mChannels.size()); + for (const auto& k : mChannels[channelID].keyframes) + { + if (k.time == time) return k; + } + throw std::runtime_error(("Animation::getKeyframe() - can't find a keyframe at time " + to_string(time)).c_str()); + } + + bool Animation::doesKeyframeExists(size_t channelID, double time) const + { + assert(channelID < mChannels.size()); + for (const auto& k : mChannels[channelID].keyframes) + { + if (k.time == time) return true; + } + return false; + } +} diff --git a/Source/Falcor/Scene/Animation/Animation.h b/Source/Falcor/Scene/Animation/Animation.h new file mode 100644 index 000000000..742fac4b5 --- /dev/null +++ b/Source/Falcor/Scene/Animation/Animation.h @@ -0,0 +1,108 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include + +namespace Falcor +{ + class AnimationController; + + class dlldecl Animation + { + public: + using SharedPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; + + struct Keyframe + { + double time = 0; + vec3 translation = vec3(0, 0, 0); + vec3 scaling = vec3(1, 1, 1); + quat rotation = quat(1, 0, 0, 0); + }; + + /** Create a new object + */ + static SharedPtr create(const std::string& name, double durationInSeconds); + + /** Get the animation's name + */ + const std::string& getName() const { return mName; } + + /** Add a new channel + */ + size_t addChannel(size_t matrixID); + + /** Get the channel count + */ + size_t getChannelCount() const { return mChannels.size(); } + + /** Add a keyframe. + If there's already a keyframe at the requested time, this call will override the existing frame + */ + void addKeyframe(size_t channelID, const Keyframe& keyframe); + + /** Get the keyframe from a specific time. + If the keyframe doesn't exists, the function will throw an exception. If you don't want to handle exceptions, call doesKeyframeExist() first + */ + const Keyframe& getKeyframe(size_t channelID, double time) const; + + /** Check if a keyframe exists in a specific time + */ + bool doesKeyframeExists(size_t channelID, double time) const; + + /** Run the animation + \param currentTime The current time in seconds. This can be larger then the animation time, in which case the animation will loop + \param matrices The array of global matrices to update + */ + void animate(double currentTime, std::vector& matrices); + + /** Get the matrixID affected by a channel + */ + size_t getChannelMatrixID(size_t channel) const { return mChannels[channel].matrixID; } + private: + Animation(const std::string& name, double durationInSeconds); + + struct Channel + { + Channel(size_t matID) : matrixID(matID) {}; + size_t matrixID; + std::vector keyframes; + size_t lastKeyframeUsed = 0; + double lastUpdateTime = 0; + }; + + std::vector mChannels; + const std::string mName; + double mDurationInSeconds = 0; + + mat4 animateChannel(Channel& c, double time); + size_t findChannelFrame(const Channel& c, double time) const; + mat4 interpolate(const Keyframe& start, const Keyframe& end, double curTime) const; + }; +} diff --git a/Source/Falcor/Scene/Animation/AnimationController.cpp b/Source/Falcor/Scene/Animation/AnimationController.cpp new file mode 100644 index 000000000..053f55406 --- /dev/null +++ b/Source/Falcor/Scene/Animation/AnimationController.cpp @@ -0,0 +1,263 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "AnimationController.h" +#include + +namespace Falcor +{ + namespace + { + const static std::string kWorldMatricesBufferName = "worldMatrices"; + const static std::string kInverseTransposeWorldMatrices = "inverseTransposeWorldMatrices"; + const static std::string kPreviousWorldMatrices = "previousFrameWorldMatrices"; + } + + AnimationController::AnimationController(Scene* pScene, const StaticVertexVector& staticVertexData, const DynamicVertexVector& dynamicVertexData) : + mpScene(pScene), mLocalMatrices(pScene->mSceneGraph.size()), mInvTransposeGlobalMatrices(pScene->mSceneGraph.size()), mMatricesChanged(pScene->mSceneGraph.size()) + { + size_t l2wBufSize = mLocalMatrices.size() * 4; + assert(l2wBufSize <= UINT32_MAX); + mpWorldMatricesBuffer = TypedBuffer::create((uint32_t)l2wBufSize); + mpPrevWorldMatricesBuffer = mpWorldMatricesBuffer; + mpInvTransposeWorldMatricesBuffer = TypedBuffer::create((uint32_t)l2wBufSize); + createSkinningPass(staticVertexData, dynamicVertexData); + } + + AnimationController::UniquePtr AnimationController::create(Scene* pScene, const StaticVertexVector& staticVertexData, const DynamicVertexVector& dynamicVertexData) + { + return UniquePtr(new AnimationController(pScene, staticVertexData, dynamicVertexData)); + } + + void AnimationController::addAnimation(uint32_t meshID, Animation::ConstSharedPtrRef pAnimation) + { + mMeshes[meshID].pAnimations.push_back(pAnimation); + mHasAnimations = true; + } + + void AnimationController::initLocalMatrices() + { + for (size_t i = 0; i < mLocalMatrices.size(); i++) mLocalMatrices[i] = mpScene->mSceneGraph[i].transform; + } + + bool AnimationController::animate(RenderContext* pContext, double currentTime) + { + PROFILE("animate"); + + mMatricesChanged.assign(mMatricesChanged.size(), false); + + if (mAnimationChanged == false) + { + if (mActiveAnimationCount == 0) return false; + if (mLastAnimationTime == currentTime) + { + // Copy the current matrices to the previous matrices. We can do that only once, but not sure if it we'll help perf (it only occures when the animation is paused) + pContext->copyResource(mpPrevWorldMatricesBuffer.get(), mpWorldMatricesBuffer.get()); + return false; + } + } + else initLocalMatrices(); + + mAnimationChanged = false; + mLastAnimationTime = currentTime; + + for (const auto& a : mMeshes) + { + const auto& mesh = a.second; + if (mesh.activeAnimation == kBindPoseAnimationId) continue; // Bind pose was pre-computed + auto& pAnimation = mesh.pAnimations[mesh.activeAnimation]; + pAnimation->animate(currentTime, mLocalMatrices); + for (size_t i = 0; i < pAnimation->getChannelCount(); i++) + { + mMatricesChanged[pAnimation->getChannelMatrixID(i)] = true; + } + } + + swap(mpPrevWorldMatricesBuffer, mpWorldMatricesBuffer); + updateMatrices(); + bindBuffers(); + executeSkinningPass(pContext); + + return true; + } + + bool AnimationController::validateIndices(uint32_t meshID, uint32_t animID, const std::string& warningPrefix) const + { + const auto& m = mMeshes.find(meshID); + if (m == mMeshes.end()) + { + logWarning(warningPrefix + " - the mesh doesn't have animations"); + return false; + } + + if (animID >= m->second.pAnimations.size()) + { + logWarning(warningPrefix + " - the animation ID doesn't exist"); + return false; + } + return true; + } + + uint32_t AnimationController::getMeshAnimationCount(uint32_t meshID) const + { + const auto& m = mMeshes.find(meshID); + return (m == mMeshes.end()) ? 0 : (uint32_t)m->second.pAnimations.size(); + } + + const std::string& AnimationController::getAnimationName(uint32_t meshID, uint32_t animID) const + { + static std::string s; + if (validateIndices(meshID, animID, "AnimationController::getAnimationName") == false) return s; + return mMeshes.at(meshID).pAnimations[animID]->getName(); + } + + bool AnimationController::setActiveAnimation(uint32_t meshID, uint32_t animID) + { + if (animID != kBindPoseAnimationId && validateIndices(meshID, animID, "AnimationController::setActiveAnimation") == false) return false; + + if (mMeshes[meshID].activeAnimation != animID) + { + mAnimationChanged = true; + mMeshes[meshID].activeAnimation = animID; + (animID != kBindPoseAnimationId) ? mActiveAnimationCount++ : mActiveAnimationCount--; + assert(mActiveAnimationCount < UINT32_MAX); + allocatePrevWorldMatrixBuffer(); + } + return true; + } + + uint32_t AnimationController::getActiveAnimation(uint32_t meshID) const + { + if (validateIndices(meshID, 0, "AnimationController::getActiveAnimation") == false) return kBindPoseAnimationId; + return mMeshes.at(meshID).activeAnimation; + } + + void AnimationController::renderUI(Gui::Widgets& widget) + { + if (mMeshes[0].pAnimations.size()) + { + bool active = mMeshes[0].activeAnimation != kBindPoseAnimationId; + if (widget.checkbox("Animate Scene", active)) + { + setActiveAnimation(0, active ? 0 : kBindPoseAnimationId); + } + } + } + + void AnimationController::updateMatrices() + { + // We can optimize this + mGlobalMatrices = mLocalMatrices; + + for (size_t i = 0; i < mGlobalMatrices.size(); i++) + { + if (mpScene->mSceneGraph[i].parent != SceneBuilder::kInvalidNode) + { + mGlobalMatrices[i] = mGlobalMatrices[mpScene->mSceneGraph[i].parent] * mGlobalMatrices[i]; + mMatricesChanged[i] = mMatricesChanged[i] || mMatricesChanged[mpScene->mSceneGraph[i].parent]; + } + + mInvTransposeGlobalMatrices[i] = transpose(inverse(mGlobalMatrices[i])); + + if(mpSkinningPass) + { + mSkinningMatrices[i] = mGlobalMatrices[i] * mpScene->mSceneGraph[i].localToBindSpace; + mInvTransposeSkinningMatrices[i] = transpose(inverse(mSkinningMatrices[i])); + } + } + mpWorldMatricesBuffer->setBlob(mGlobalMatrices.data(), 0, mpWorldMatricesBuffer->getSize()); + mpInvTransposeWorldMatricesBuffer->setBlob(mInvTransposeGlobalMatrices.data(), 0, mpInvTransposeWorldMatricesBuffer->getSize()); + } + + void AnimationController::bindBuffers() + { + ParameterBlock* pBlock = mpScene->mpSceneBlock.get(); + pBlock->setTypedBuffer(kWorldMatricesBufferName, mpWorldMatricesBuffer); + pBlock->setTypedBuffer(kPreviousWorldMatrices, mpPrevWorldMatricesBuffer); + pBlock->setTypedBuffer(kInverseTransposeWorldMatrices, mpInvTransposeWorldMatricesBuffer); + } + + void AnimationController::allocatePrevWorldMatrixBuffer() + { + if (mActiveAnimationCount) + { + if(mpWorldMatricesBuffer == mpPrevWorldMatricesBuffer) + { + mpPrevWorldMatricesBuffer = TypedBuffer::create(mpWorldMatricesBuffer->getElementCount()); + } + } + else mpPrevWorldMatricesBuffer = mpWorldMatricesBuffer; + } + + void AnimationController::createSkinningPass(const std::vector& staticVertexData, const std::vector& dynamicVertexData) + { + StructuredBuffer::SharedPtr pVB = mpScene->mpVao->getVertexBuffer(Scene::kStaticDataBufferIndex)->asStructuredBuffer(); + assert(pVB->getSize() == staticVertexData.size() * sizeof(staticVertexData[0])); + // We always copy the static data, to initialize the non-skinned vertices + pVB->setBlob(staticVertexData.data(), 0, pVB->getSize()); + pVB->uploadToGPU(); + + if (dynamicVertexData.size()) + { + mSkinningMatrices.resize(mpScene->mSceneGraph.size()); + mInvTransposeSkinningMatrices.resize(mSkinningMatrices.size()); + + mpSkinningPass = ComputePass::create("Skinning.slang"); + auto pBlock = mpSkinningPass->getVars()->getParameterBlock("gData"); + pBlock->setStructuredBuffer("skinnedVertices", pVB); + + auto createBuffer = [&](const std::string& name, const auto& initData) + { + ReflectionResourceType::SharedConstPtr pReflector = pBlock->getReflection()->getResource(name)->getType()->asResourceType()->shared_from_this(); + auto pBuffer = StructuredBuffer::create(name, pReflector, (uint32_t)initData.size(), ResourceBindFlags::ShaderResource); + pBuffer->setBlob(initData.data(), 0, pBuffer->getSize()); + pBlock->setStructuredBuffer(name, pBuffer); + }; + + createBuffer("staticData", staticVertexData); + createBuffer("dynamicData", dynamicVertexData); + + mpSkinningMatricesBuffer = TypedBuffer::create((uint32_t)mSkinningMatrices.size() * 4, ResourceBindFlags::ShaderResource); + mpInvTransposeSkinningMatricesBuffer = TypedBuffer::create((uint32_t)mSkinningMatrices.size() * 4, ResourceBindFlags::ShaderResource); + pBlock->setTypedBuffer("boneMatrices", mpSkinningMatricesBuffer); + pBlock->setTypedBuffer("inverseTransposeBoneMatrices", mpInvTransposeSkinningMatricesBuffer); + pBlock->setTypedBuffer("inverseTransposeWorldMatrices", mpInvTransposeWorldMatricesBuffer); + pBlock->setTypedBuffer("worldMatrices", mpWorldMatricesBuffer); + + mSkinningDispatchSize = (uint32_t)dynamicVertexData.size(); + } + } + + void AnimationController::executeSkinningPass(RenderContext* pContext) + { + if (!mpSkinningPass) return; + mpSkinningMatricesBuffer->setBlob(mSkinningMatrices.data(), 0, mpSkinningMatricesBuffer->getSize()); + mpInvTransposeSkinningMatricesBuffer->setBlob(mInvTransposeSkinningMatrices.data(), 0, mpInvTransposeSkinningMatricesBuffer->getSize()); + mpSkinningPass->execute(pContext, mSkinningDispatchSize, 1, 1); + } +} diff --git a/Source/Falcor/Scene/Animation/AnimationController.h b/Source/Falcor/Scene/Animation/AnimationController.h new file mode 100644 index 000000000..5008a2b4c --- /dev/null +++ b/Source/Falcor/Scene/Animation/AnimationController.h @@ -0,0 +1,152 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Animation.h" +#include "RenderGraph/BasePasses/ComputePass.h" + +namespace Falcor +{ + class Scene; + + struct Bone + { + uint32_t parentID; + uint32_t boneID; + std::string name; + glm::mat4 offset; + glm::mat4 localTransform; + glm::mat4 originalLocalTransform; + glm::mat4 globalTransform; + }; + + class Model; + class AssimpModelImporter; + + class dlldecl AnimationController + { + public: + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + static const uint32_t kBindPoseAnimationId = -1; + static const uint32_t kInvalidBoneID = -1; + ~AnimationController() = default; + + using StaticVertexVector = std::vector; + using DynamicVertexVector = std::vector; + + /** Create a new object + */ + static UniquePtr create(Scene* pScene, const StaticVertexVector& staticVertexData, const DynamicVertexVector& dynamicVertexData); + + /** Add an animation for a mesh + */ + void addAnimation(uint32_t meshID, Animation::ConstSharedPtrRef pAnimation); + + /** Run the animation + \return true if a change occurred, otherwise false + */ + bool animate(RenderContext* pContext, double currentTime); + + /** Get number of animations for a mesh + */ + uint32_t getMeshAnimationCount(uint32_t meshID) const; + + /** Get the name of a mesh's animation + If the animation doesn't exist it will return an empty string. + Don't use this function to detect if an animation exists or not. Use `getMeshAnimationCount()` instead + */ + const std::string& getAnimationName(uint32_t meshID, uint32_t animID) const; + + /** Set a mesh active animation. Use kBindPoseAnimationId to disable animations for the mesh + \If the mesh/animation exists, will return true. Otherwise returns false + */ + bool setActiveAnimation(uint32_t meshID, uint32_t animID); + + /** Get a mesh's active animation + If no animations exist for the mesh, will return kBindPoseAnimationId + */ + uint32_t getActiveAnimation(uint32_t meshID) const; + + /** Whether the controller is handling any animations. + */ + bool hasAnimations() const { return mHasAnimations; } + + /** Render the UI + */ + void renderUI(Gui::Widgets& widget); + + /** Get the global matrices + */ + const std::vector& getGlobalMatrices() const { return mGlobalMatrices; } + + /** Check if a matrix changed + */ + bool didMatrixChanged(size_t matrixID) const { return mMatricesChanged[matrixID]; } + private: + friend class SceneBuilder; + AnimationController(Scene* pScene, const StaticVertexVector& staticVertexData, const DynamicVertexVector& dynamicVertexData); + + void allocatePrevWorldMatrixBuffer(); + void bindBuffers(); + void updateMatrices(); + bool validateIndices(uint32_t meshID, uint32_t animID, const std::string& warningPrefix) const; + + struct MeshAnimation + { + std::vector pAnimations; + uint32_t activeAnimation = kBindPoseAnimationId; + }; + + std::map mMeshes; + std::vector mLocalMatrices; + std::vector mGlobalMatrices; + std::vector mInvTransposeGlobalMatrices; + std::vector mMatricesChanged; + + bool mHasAnimations = false; + bool mAnimationChanged = true; + uint32_t mActiveAnimationCount = 0; + double mLastAnimationTime = 0; + Scene* mpScene = nullptr; + + TypedBuffer::SharedPtr mpWorldMatricesBuffer; + TypedBuffer::SharedPtr mpPrevWorldMatricesBuffer; + TypedBuffer::SharedPtr mpInvTransposeWorldMatricesBuffer; + + // Skinning + ComputePass::SharedPtr mpSkinningPass; + std::vector mSkinningMatrices; + std::vector mInvTransposeSkinningMatrices; + uint32_t mSkinningDispatchSize = 0; + void createSkinningPass(const std::vector& staticVertexData, const std::vector& dynamicVertexData); + void executeSkinningPass(RenderContext* pContext); + TypedBuffer::SharedPtr mpSkinningMatricesBuffer; + TypedBuffer::SharedPtr mpInvTransposeSkinningMatricesBuffer; + void initLocalMatrices(); + }; +} diff --git a/Framework/Source/Graphics/Camera/Camera.cpp b/Source/Falcor/Scene/Camera/Camera.cpp similarity index 72% rename from Framework/Source/Graphics/Camera/Camera.cpp rename to Source/Falcor/Scene/Camera/Camera.cpp index eb86a3023..07a5211a3 100644 --- a/Framework/Source/Graphics/Camera/Camera.cpp +++ b/Source/Falcor/Scene/Camera/Camera.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Camera.h" -#include "Utils/AABB.h" +#include "Utils/Math/AABB.h" #include "Utils/Math/FalcorMath.h" -#include "API/ConstantBuffer.h" -#include "Utils/Gui.h" +#include "Core/BufferTypes/ConstantBuffer.h" +#include "Utils/UI/Gui.h" namespace Falcor { @@ -49,7 +49,7 @@ namespace Falcor return SharedPtr(pCamera); } - void Camera::beginFrame() + Camera::Changes Camera::beginFrame(bool firstFrame) { if (mJitterPattern.pGenerator) { @@ -58,8 +58,38 @@ namespace Falcor setJitterInternal(jitter.x, jitter.y); } - mData.prevViewProjMat = mViewProjMatNoJitter; - mData.rightEyePrevViewProjMat = mData.rightEyeViewProjMat; + calculateCameraParameters(); + + if (firstFrame) mPrevData = mData; + + // Keep copies of the transforms used for the previous frame. We need these for computing motion vectors etc. + mData.prevViewProjMatNoJitter = mPrevData.viewProjMatNoJitter; + mData.rightEyePrevViewProjMat = mPrevData.rightEyeViewProjMat; + + mChanges = is_set(mChanges, Changes::Movement | Changes::Frustum) ? Changes::History : Changes::None; + + if (mPrevData.posW != mData.posW) mChanges |= Changes::Movement; + if (mPrevData.up != mData.up) mChanges |= Changes::Movement; + if (mPrevData.target != mData.target) mChanges |= Changes::Movement; + + if (mPrevData.focalDistance != mData.focalDistance) mChanges |= Changes::FocalDistance; + if (mPrevData.apertureRadius != mData.apertureRadius) mChanges |= Changes::Aperture | Changes::Exposure; + if (mPrevData.shutterSpeed != mData.shutterSpeed) mChanges |= Changes::Exposure; + if (mPrevData.ISOSpeed != mData.ISOSpeed) mChanges |= Changes::Exposure; + + if (mPrevData.focalLength != mData.focalLength) mChanges |= Changes::Frustum; + if (mPrevData.aspectRatio != mData.aspectRatio) mChanges |= Changes::Frustum; + if (mPrevData.nearZ != mData.nearZ) mChanges |= Changes::Frustum; + if (mPrevData.farZ != mData.farZ) mChanges |= Changes::Frustum; + if (mPrevData.frameHeight != mData.frameHeight) mChanges |= Changes::Frustum; + + // Jitter + if (mPrevData.jitterX != mData.jitterX) mChanges |= Changes::Jitter; + if (mPrevData.jitterY != mData.jitterY) mChanges |= Changes::Jitter; + + mPrevData = mData; + + return getChanges(); } void Camera::calculateCameraParameters() const @@ -105,7 +135,7 @@ namespace Falcor 0.0f, 0.0f, 1.0f, 0.0f, 2.0f * mData.jitterX, 2.0f * mData.jitterY, 0.0f, 1.0f); // Apply jitter matrix to the projection matrix - mViewProjMatNoJitter = mData.projMat * mData.viewMat; + mData.viewProjMatNoJitter = mData.projMat * mData.viewMat; mData.projMat = jitterMat * mData.projMat; mData.viewProjMat = mData.projMat * mData.viewMat; @@ -233,14 +263,7 @@ namespace Falcor pBuffer->setBlob(&mData, offset, getShaderDataSize()); } - void Camera::move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) - { - setPosition(position); - setTarget(target); - setUpVector(up); - } - - void Camera::setPatternGenerator(const PatternGenerator::SharedPtr& pGenerator, const vec2& scale) + void Camera::setPatternGenerator(const CPUSampleGenerator::SharedPtr& pGenerator, const vec2& scale) { mJitterPattern.pGenerator = pGenerator; mJitterPattern.scale = scale; @@ -269,27 +292,47 @@ namespace Falcor void Camera::renderUI(Gui* pGui, const char* uiGroup) { - if (uiGroup == nullptr || pGui->beginGroup(uiGroup)) + if (!uiGroup) uiGroup = "Camera Settings"; + + auto g = Gui::Group(pGui, uiGroup); + if (g.open()) { float focalLength = getFocalLength(); - if (pGui->addFloatVar("Focal Length", focalLength, 0.0f, FLT_MAX, 0.25f)) setFocalLength(focalLength); + if (g.var("Focal Length", focalLength, 0.0f, FLT_MAX, 0.25f)) setFocalLength(focalLength); float aspectRatio = getAspectRatio(); - if (pGui->addFloatVar("Aspect Ratio", aspectRatio, 0, FLT_MAX, 0.001f)) setAspectRatio(aspectRatio); + if (g.var("Aspect Ratio", aspectRatio, 0.f, FLT_MAX, 0.001f)) setAspectRatio(aspectRatio); + + float focalDistance = getFocalDistance(); + if (g.var("Focal Distance", focalDistance, 0.f, FLT_MAX, 0.05f)) setFocalDistance(focalDistance); + + float apertureRadius = getApertureRadius(); + if (g.var("Aperture Radius", apertureRadius, 0.f, FLT_MAX, 0.001f)) setApertureRadius(apertureRadius); + + float shutterSpeed = getShutterSpeed(); + if (g.var("Shutter Speed", shutterSpeed, 0.f, FLT_MAX, 0.001f)) setShutterSpeed(shutterSpeed); + + float ISOSpeed = getISOSpeed(); + if (g.var("ISO Speed", ISOSpeed, 0.8f, FLT_MAX, 0.25f)) setISOSpeed(ISOSpeed); float2 depth(getNearPlane(), getFarPlane()); - if (pGui->addFloat2Var("Depth Range", depth, 0, FLT_MAX, 0.1f)) setDepthRange(depth.x, depth.y); + if (g.var("Depth Range", depth, 0.f, FLT_MAX, 0.1f)) setDepthRange(depth.x, depth.y); float3 pos = getPosition(); - if (pGui->addFloat3Var("Position", pos)) setPosition(pos); + if (g.var("Position", pos, -FLT_MAX, FLT_MAX, 0.001f)) setPosition(pos); float3 target = getTarget(); - if (pGui->addFloat3Var("Target", target)) setTarget(target); + if (g.var("Target", target, -FLT_MAX, FLT_MAX, 0.001f)) setTarget(target); - float3 up = getTarget(); - if (pGui->addFloat3Var("Up", up)) setUpVector(up); + float3 up = getUpVector(); + if (g.var("Up", up, -FLT_MAX, FLT_MAX, 0.001f)) setUpVector(up); - if (uiGroup) pGui->endGroup(); - } + g.release(); + } + } + + SCRIPT_BINDING(Camera) + { + m.regClass(Camera); } } diff --git a/Framework/Source/Graphics/Camera/Camera.h b/Source/Falcor/Scene/Camera/Camera.h similarity index 81% rename from Framework/Source/Graphics/Camera/Camera.h rename to Source/Falcor/Scene/Camera/Camera.h index bca8da460..34749f572 100644 --- a/Framework/Source/Graphics/Camera/Camera.h +++ b/Source/Falcor/Scene/Camera/Camera.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,13 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include "glm/geometric.hpp" -#include "glm/mat4x4.hpp" #include "Data/HostDeviceData.h" -#include -#include "Graphics/Paths/MovableObject.h" -#include "Utils/PatternGenerators/PatternGenerator.h" +#include "Utils/SampleGenerators/CPUSampleGenerator.h" namespace Falcor { @@ -42,12 +37,12 @@ namespace Falcor /** Camera class. Default transform matrices are interpreted as left eye transform during stereo rendering. */ - class Camera : public IMovableObject, public inherit_shared_from_this + class dlldecl Camera { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - SharedPtr shared_from_this() { return inherit_shared_from_this::shared_from_this(); } + using ConstSharedPtrRef = const SharedPtr&; // Default dimensions of full frame cameras and 35mm film static const float kDefaultFrameHeight; @@ -65,7 +60,7 @@ namespace Falcor */ const std::string& getName() const { return mName; } - /** Set the camera's aspect ratio (width/height). + /** Set the camera's aspect ratio. */ void setAspectRatio(float aspectRatio) { mData.aspectRatio = aspectRatio; mDirty = true; } @@ -105,6 +100,22 @@ namespace Falcor */ float getApertureRadius() const { return mData.apertureRadius; } + /** Set camera shutter speed in seconds. + */ + void setShutterSpeed(float shutterSpeed) { mData.shutterSpeed = shutterSpeed; mDirty = true; } + + /** Get camera shutter speed in seconds. + */ + float getShutterSpeed() const { return mData.shutterSpeed; } + + /** Set camera's film speed based on ISO standards. + */ + void setISOSpeed(float ISOSpeed) { mData.ISOSpeed = ISOSpeed; mDirty = true; } + + /** Get camera's film speed based on ISO standards. + */ + float getISOSpeed() const { return mData.ISOSpeed; } + /** Get the camera's world space position. */ const glm::vec3& getPosition() const { return mData.posW; } @@ -129,10 +140,6 @@ namespace Falcor */ void setTarget(const glm::vec3& target) { mData.target = target; mDirty = true; } - /** IMovable object interface. - */ - void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override; - /** Set the camera's depth range. */ void setDepthRange(float nearZ, float farZ) { mData.farZ = farZ; mData.nearZ = nearZ; mDirty = true; } @@ -147,15 +154,15 @@ namespace Falcor /** Set a pattern generator. If a generator is set, then a jitter will be set every frame based on the generator */ - void setPatternGenerator(const PatternGenerator::SharedPtr& pGenerator, const vec2& scale = vec2(1)); + void setPatternGenerator(const CPUSampleGenerator::SharedPtr& pGenerator, const vec2& scale = vec2(1)); /** Get the bound pattern generator */ - const PatternGenerator::SharedPtr& getPatternGenerator() const { return mJitterPattern.pGenerator; } + const CPUSampleGenerator::SharedPtr& getPatternGenerator() const { return mJitterPattern.pGenerator; } /** Set the camera's jitter. - \param[in] jitterX Subpixel offset along X axis divided by screen width - \param[in] jitterY Subpixel offset along Y axis divided by screen height + \param[in] jitterX Subpixel offset along X axis divided by screen width (positive value shifts the image right). + \param[in] jitterY Subpixel offset along Y axis divided by screen height (positive value shifts the image up). */ void setJitter(float jitterX, float jitterY); float getJitterX() const { return mData.jitterX; } @@ -232,7 +239,7 @@ namespace Falcor /** Get the size of the CameraData struct in bytes. */ - static uint32_t getShaderDataSize() + static uint32_t getShaderDataSize() { static const size_t dataSize = sizeof(CameraData); static_assert(dataSize % (sizeof(vec4)) == 0, "Camera::CameraData size should be a multiple of 16"); @@ -243,11 +250,32 @@ namespace Falcor */ void renderUI(Gui* pGui, const char* uiGroup = nullptr); - /** Begin frame. Should be called once per-frame, this is where we store the previous frame matrices + enum class Changes + { + None = 0x0, + Movement = 0x1, + Exposure = 0x2, + FocalDistance = 0x4, + Jitter = 0x8, + Frustum = 0x10, + Aperture = 0x20, + History = 0x40, ///< The previous frame matrix changed. This indicates that the camera motion-vectors changed + }; + + + /** Begin frame. Should be called once at the start of each frame. + This is where we store the previous frame matrices. + \param[in] firstFrame Set to true on the first frame or after switching cameras. */ - void beginFrame(); + Changes beginFrame(bool firstFrame = false); + + /** Get the camera changes that happened in since the previous frame + */ + Changes getChanges() const { return mChanges; } + private: Camera(); + Changes mChanges = Changes::None; mutable bool mDirty = true; mutable bool mEnablePersistentProjMat = false; @@ -259,7 +287,7 @@ namespace Falcor void calculateCameraParameters() const; mutable CameraData mData; - mutable glm::mat4 mViewProjMatNoJitter; + CameraData mPrevData; struct { @@ -270,10 +298,12 @@ namespace Falcor struct { - PatternGenerator::SharedPtr pGenerator; + CPUSampleGenerator::SharedPtr pGenerator; vec2 scale; } mJitterPattern; void setJitterInternal(float jitterX, float jitterY); }; -} \ No newline at end of file + + enum_class_operators(Camera::Changes); +} diff --git a/Framework/Source/Graphics/Camera/CameraController.cpp b/Source/Falcor/Scene/Camera/CameraController.cpp similarity index 69% rename from Framework/Source/Graphics/Camera/CameraController.cpp rename to Source/Falcor/Scene/Camera/CameraController.cpp index b4e20497d..ff9c989ee 100644 --- a/Framework/Source/Graphics/Camera/CameraController.cpp +++ b/Source/Falcor/Scene/Camera/CameraController.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "CameraController.h" -#include "Camera.h" +#include "Utils/UI/UserInput.h" #include "Utils/Math/FalcorMath.h" - -#include "VR/OpenVR/VRSystem.h" +#include "Camera.h" namespace Falcor { @@ -43,7 +42,7 @@ namespace Falcor return res; } - void ModelViewCameraController::setModelParams(const glm::vec3& center, float radius, float distanceInRadius) + void OrbiterCameraController::setModelParams(const glm::vec3& center, float radius, float distanceInRadius) { mModelCenter = center; mModelRadius = radius; @@ -52,7 +51,7 @@ namespace Falcor mbDirty = true; } - bool ModelViewCameraController::onMouseEvent(const MouseEvent& mouseEvent) + bool OrbiterCameraController::onMouseEvent(const MouseEvent& mouseEvent) { bool handled = false; switch(mouseEvent.type) @@ -91,7 +90,7 @@ namespace Falcor return handled; } - bool ModelViewCameraController::update() + bool OrbiterCameraController::update() { if(mpCamera && mbDirty) { @@ -112,7 +111,7 @@ namespace Falcor } template - FirstPersonCameraControllerCommon::FirstPersonCameraControllerCommon() + FirstPersonCameraControllerCommon::FirstPersonCameraControllerCommon(Camera::ConstSharedPtrRef pCamera) : CameraController(pCamera) { mTimer.update(); } @@ -225,7 +224,7 @@ namespace Falcor glm::vec3 viewDir = normalize(camTarget - camPos); glm::vec3 sideway = glm::cross(viewDir, normalize(camUp)); - float elapsedTime = mTimer.getElapsedTime(); + float elapsedTime = (float)mTimer.delta(); float curMove = mSpeedModifier * mSpeed * elapsedTime; camPos += movement.z * curMove * viewDir; @@ -285,106 +284,4 @@ namespace Falcor template class FirstPersonCameraControllerCommon < true > ; template class FirstPersonCameraControllerCommon < false > ; - - void HmdCameraController::attachCamera(const Camera::SharedPtr& pCamera) - { - if(VRSystem::instance() == nullptr) - { - logError("Can't attach camera to HmdCameraController. VRSystem not initialized", true); - return; - } - - if(mpCamera == pCamera) - { - return; - } - detachCamera(); - - if (pCamera) - { - CameraController::attachCamera(pCamera); - - // Store the original camera parameters - mOrigFocalLength = pCamera->getFocalLength(); - mOrigAspectRatio = pCamera->getAspectRatio(); - - // Initialize the parameters from the HMD - VRDisplay* pDisplay = VRSystem::instance()->getHMD().get(); - - pCamera->setFocalLength(fovYToFocalLength(pDisplay->getFovY(), pCamera->getFrameHeight())); - pCamera->setAspectRatio(pDisplay->getAspectRatio()); - - mInvPrevHmdViewMat = glm::mat4(); - } - } - - void setCameraParamsFromViewMat(Camera* pCamera, const glm::mat4& viewMat) - { - const glm::mat4 invViewMat = glm::inverse(viewMat); - glm::vec4 hmdPos = invViewMat * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - glm::vec3 hmdTarget = glm::mat3(invViewMat) * glm::vec3(0.0f, 0.0f, -1.0f); - glm::vec3 hmdUp = glm::mat3(invViewMat) * glm::vec3(0.0f, 1.0f, 0.0f); - pCamera->setPosition(glm::vec3(hmdPos)); - pCamera->setTarget(hmdTarget + glm::vec3(hmdPos)); - pCamera->setUpVector(hmdUp); - } - - bool HmdCameraController::update() - { - if(mpCamera) - { - VRDisplay* pDisplay = VRSystem::instance()->getHMD().get(); - - // Set the near/far planes - pDisplay->setDepthRange(mpCamera->getNearPlane(), mpCamera->getFarPlane()); - - // Calculate the HMD world space position - const glm::mat4 hmdWorldMat = pDisplay->getWorldMatrix(); - glm::mat4 viewMat = hmdWorldMat * mInvPrevHmdViewMat * mpCamera->getViewMatrix(); - - // Calculate the view params based on the center matrix - setCameraParamsFromViewMat(mpCamera.get(), viewMat); - - // Update based on the mouse/keyboard movement - if(SixDoFCameraController::update()) - { - mpCamera->togglePersistentViewMatrix(false); - viewMat = mpCamera->getViewMatrix(); - } - - // Get the right eye matrix - glm::mat4 rightEyeView = pDisplay->getOffsetMatrix(VRDisplay::Eye::Right) * viewMat; - glm::mat4 rightEyeProj = pDisplay->getProjectionMatrix(VRDisplay::Eye::Right); - mpCamera->setRightEyeMatrices(rightEyeView, rightEyeProj); - - // Get the left eye matrix - mpCamera->setProjectionMatrix(pDisplay->getProjectionMatrix(VRDisplay::Eye::Left)); - glm::mat4 leftEyeOffset = pDisplay->getOffsetMatrix(VRDisplay::Eye::Left); - mpCamera->setViewMatrix(leftEyeOffset * viewMat); - mInvPrevHmdViewMat = glm::inverse(leftEyeOffset * hmdWorldMat); - } - return true; - } - - void HmdCameraController::detachCamera() - { - if(mpCamera) - { - // Calculate the view params based on the center matrix - setCameraParamsFromViewMat(mpCamera.get(), mInvPrevHmdViewMat * mpCamera->getViewMatrix()); - - // Restore the original parameters - mpCamera->setFocalLength(mOrigFocalLength); - mpCamera->setAspectRatio(mOrigAspectRatio); - mpCamera->togglePersistentProjectionMatrix(false); - mpCamera->togglePersistentViewMatrix(false); - - mpCamera = nullptr; - } - } - - HmdCameraController::~HmdCameraController() - { - detachCamera(); - } -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Camera/CameraController.h b/Source/Falcor/Scene/Camera/CameraController.h similarity index 80% rename from Framework/Source/Graphics/Camera/CameraController.h rename to Source/Falcor/Scene/Camera/CameraController.h index 9a0a8e8b5..0a6a52591 100644 --- a/Framework/Source/Graphics/Camera/CameraController.h +++ b/Source/Falcor/Scene/Camera/CameraController.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,27 +26,22 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec3.hpp" -#include "glm/mat3x3.hpp" -#include "Utils/UserInput.h" -#include "Utils/CpuTimer.h" -#include "Graphics/Camera/Camera.h" #include +#include "Camera.h" namespace Falcor { + struct MouseEvent; + struct KeyboardEvent; + /** Camera controller interface. Camera controllers should inherit from this object. */ - class CameraController + class dlldecl CameraController { public: using SharedPtr = std::shared_ptr; virtual ~CameraController() = default; - /** Attach a camera to this controller - */ - virtual void attachCamera(const Camera::SharedPtr& pCamera) { mpCamera = pCamera; }; - /** Handle mouse events */ virtual bool onMouseEvent(const MouseEvent& mouseEvent) { return false; } @@ -66,19 +61,27 @@ namespace Falcor void setCameraSpeed(float speed) { mSpeed = speed; } protected: - CameraController() = default; + CameraController(Camera::ConstSharedPtrRef pCamera) : mpCamera(pCamera) {} Camera::SharedPtr mpCamera = nullptr; float mSpeed = 1; }; - /** Model-view camera controller. Orbits around a given point. + /** An orbiter camera controller. Orbits around a given point. To control the camera: * Left mouse click + movement will orbit around the model. * Mouse wheel zooms in/out. */ - class ModelViewCameraController : public CameraController + class dlldecl OrbiterCameraController : public CameraController { public: + using SharedPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; + OrbiterCameraController(Camera::ConstSharedPtrRef pCamera) : CameraController(pCamera) {} + + /** Create a new object + */ + static SharedPtr create(Camera::ConstSharedPtrRef pCamera) { return SharedPtr(new OrbiterCameraController(pCamera)); } + /** Handle mouse events */ bool onMouseEvent(const MouseEvent& mouseEvent) override; @@ -118,10 +121,16 @@ namespace Falcor - Ctrl for slower movement. */ template - class FirstPersonCameraControllerCommon : public CameraController + class dlldecl FirstPersonCameraControllerCommon : public CameraController { public: - FirstPersonCameraControllerCommon(); + FirstPersonCameraControllerCommon(Camera::ConstSharedPtrRef pCamera); + using SharedPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; + + /** Create a new object + */ + static SharedPtr create(Camera::ConstSharedPtrRef pCamera) { return SharedPtr(new FirstPersonCameraControllerCommon(pCamera)); } /** Handle mouse events */ @@ -164,28 +173,4 @@ namespace Falcor using FirstPersonCameraController = FirstPersonCameraControllerCommon; using SixDoFCameraController = FirstPersonCameraControllerCommon; - - /** Queries the VR system to control cameras based on the HMD position and orientation - */ - class HmdCameraController : public SixDoFCameraController - { - public: - - /** Attach a camera to this controller - */ - virtual void attachCamera(const Camera::SharedPtr& pCamera) override; - - ~HmdCameraController(); - - /** Update the camera position and orientation. - \return Whether the camera was updated/changed - */ - bool update() override; - - private: - void detachCamera(); - float mOrigFocalLength; - float mOrigAspectRatio; - glm::mat4 mInvPrevHmdViewMat; - }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Scene/Importers/AssimpImporter.cpp b/Source/Falcor/Scene/Importers/AssimpImporter.cpp new file mode 100644 index 000000000..80d2466e3 --- /dev/null +++ b/Source/Falcor/Scene/Importers/AssimpImporter.cpp @@ -0,0 +1,1061 @@ +#/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "assimp/Importer.hpp" +#include "assimp/postprocess.h" +#include "assimp/scene.h" +#include "assimp/pbrmaterial.h" +#include "AssimpImporter.h" +#include "Utils/StringUtils.h" +#include "Core/API/Device.h" +#include "Scene/SceneBuilder.h" + +namespace Falcor +{ + namespace + { + using BoneMeshMap = std::map>; + using MeshInstanceList = std::vector>; + + enum class ImportMode { + Default, + OBJ, + GLTF2, + }; + + mat4 aiCast(const aiMatrix4x4& aiMat) + { + mat4 glmMat; + glmMat[0][0] = aiMat.a1; glmMat[0][1] = aiMat.a2; glmMat[0][2] = aiMat.a3; glmMat[0][3] = aiMat.a4; + glmMat[1][0] = aiMat.b1; glmMat[1][1] = aiMat.b2; glmMat[1][2] = aiMat.b3; glmMat[1][3] = aiMat.b4; + glmMat[2][0] = aiMat.c1; glmMat[2][1] = aiMat.c2; glmMat[2][2] = aiMat.c3; glmMat[2][3] = aiMat.c4; + glmMat[3][0] = aiMat.d1; glmMat[3][1] = aiMat.d2; glmMat[3][2] = aiMat.d3; glmMat[3][3] = aiMat.d4; + + return transpose(glmMat); + } + + vec3 aiCast(const aiColor3D& ai) + { + return vec3(ai.r, ai.g, ai.b); + } + + vec3 aiCast(const aiVector3D& val) + { + return vec3(val.x, val.y, val.z); + } + + quat aiCast(const aiQuaternion& q) + { + return quat(q.w, q.x, q.y, q.z); + } + + void setTexture(aiTextureType type, ImportMode importMode, Material* pMaterial, Texture::SharedPtr pTexture) + { + switch (type) + { + case aiTextureType_DIFFUSE: + pMaterial->setBaseColorTexture(pTexture); + break; + case aiTextureType_SPECULAR: + pMaterial->setSpecularTexture(pTexture); + break; + case aiTextureType_EMISSIVE: + pMaterial->setEmissiveTexture(pTexture); + break; + case aiTextureType_HEIGHT: + case aiTextureType_DISPLACEMENT: + // OBJ doesn't support normal maps, so they are usually placed in the height map slot. For consistency with other formats, we move them to the normal map slot. + importMode == ImportMode::OBJ ? pMaterial->setNormalMap(pTexture) : pMaterial->setHeightMap(pTexture); + break; + case aiTextureType_NORMALS: + pMaterial->setNormalMap(pTexture); + break; + case aiTextureType_AMBIENT: + pMaterial->setOcclusionMap(pTexture); + break; + case aiTextureType_LIGHTMAP: + pMaterial->setLightMap(pTexture); + break; + default: + logWarning("Unsupported Assimp texture type " + std::to_string(type)); + } + } + + bool isSrgbRequired(aiTextureType aiType, bool isSrgbRequested, uint32_t shadingModel) + { + if (isSrgbRequested == false) + { + return false; + } + + switch (aiType) + { + case aiTextureType_SPECULAR: + assert(shadingModel == ShadingModelMetalRough || shadingModel == ShadingModelSpecGloss); + return (shadingModel == ShadingModelSpecGloss); + case aiTextureType_DIFFUSE: + case aiTextureType_AMBIENT: + case aiTextureType_EMISSIVE: + case aiTextureType_LIGHTMAP: + case aiTextureType_REFLECTION: + return true; + case aiTextureType_HEIGHT: + case aiTextureType_NORMALS: + case aiTextureType_SHININESS: + case aiTextureType_OPACITY: + case aiTextureType_DISPLACEMENT: + return false; + default: + should_not_get_here(); + return false; + } + } + + class ImporterData + { + public: + ImporterData(const aiScene* pAiScene, SceneBuilder& sceneBuilder, const SceneBuilder::InstanceMatrices& modelInstances_) : pScene(pAiScene), modelInstances(modelInstances_), builder(sceneBuilder) {} + const aiScene* pScene; + + SceneBuilder& builder; + std::map materialMap; + std::map meshMap; + std::map textureCache; + const SceneBuilder::InstanceMatrices& modelInstances; + std::map localToBindPoseMatrices; + + size_t getFalcorNode(const aiNode* pNode) const + { + return mAiToFalcorNode.at(pNode); + } + + size_t getFalcorNode(const std::string& aiNodeName, uint32_t index) const + { + try + { + return getFalcorNode(mAiNodes.at(aiNodeName)[index]); + } + catch (std::exception) + { + return SceneBuilder::kInvalidNode; + } + } + + uint32_t getNodeInstanceCount(const std::string& nodeName) const + { + return (uint32_t)mAiNodes.at(nodeName).size(); + } + + void addAiNode(const aiNode* pNode, size_t falcorID) + { + assert(mAiToFalcorNode.find(pNode) == mAiToFalcorNode.end()); + mAiToFalcorNode[pNode] = falcorID; + + if (mAiNodes.find(pNode->mName.C_Str()) == mAiNodes.end()) + { + mAiNodes[pNode->mName.C_Str()] = {}; + } + mAiNodes[pNode->mName.C_Str()].push_back(pNode); + } + + private: + std::map mAiToFalcorNode; + std::map> mAiNodes; + + }; + + using KeyframeList = std::list; + + struct AnimationChannelData + { + uint32_t posIndex = 0; + uint32_t rotIndex = 0; + uint32_t scaleIndex = 0; + }; + + template + bool parseAnimationChannel(const AiType* pKeys, uint32_t count, double time, uint32_t& currentIndex, FalcorType& falcor) + { + if (currentIndex >= count) return true; + + if (pKeys[currentIndex].mTime == time) + { + falcor = aiCast(pKeys[currentIndex].mValue); + currentIndex++; + } + + return currentIndex >= count; + } + + void resetNegativeKeyframeTimes(aiNodeAnim* pAiNode) + { + auto resetTime = [](auto keys, uint32_t count) + { + if (count > 1) assert(keys[1].mTime >= 0); + if (keys[0].mTime < 0) keys[0].mTime = 0; + }; + resetTime(pAiNode->mPositionKeys, pAiNode->mNumPositionKeys); + resetTime(pAiNode->mRotationKeys, pAiNode->mNumRotationKeys); + resetTime(pAiNode->mScalingKeys, pAiNode->mNumScalingKeys); + } + + Animation::SharedPtr createAnimation(ImporterData& data, const aiAnimation* pAiAnim) + { + assert(pAiAnim->mNumMeshChannels == 0); + double duration = pAiAnim->mDuration; + double ticksPerSecond = pAiAnim->mTicksPerSecond ? pAiAnim->mTicksPerSecond : 25; + double durationInSeconds = duration / ticksPerSecond; + + Animation::SharedPtr pAnimation = Animation::create(pAiAnim->mName.C_Str(), durationInSeconds); + + for (uint32_t i = 0; i < pAiAnim->mNumChannels; i++) + { + aiNodeAnim* pAiNode = pAiAnim->mChannels[i]; + resetNegativeKeyframeTimes(pAiNode); + + std::vector channels; + for (uint32_t i = 0; i < data.getNodeInstanceCount(pAiNode->mNodeName.C_Str()); i++) + { + channels.push_back(pAnimation->addChannel(data.getFalcorNode(pAiNode->mNodeName.C_Str(), i))); + } + + uint32_t pos = 0, rot = 0, scale = 0; + Animation::Keyframe keyframe; + bool done = false; + + auto nextKeyTime = [&]() + { + double time = -std::numeric_limits::max(); + if (pos < pAiNode->mNumPositionKeys) time = max(time, pAiNode->mPositionKeys[pos].mTime); + if (rot < pAiNode->mNumRotationKeys) time = max(time, pAiNode->mRotationKeys[rot].mTime); + if (scale < pAiNode->mNumScalingKeys) time = max(time, pAiNode->mScalingKeys[scale].mTime); + assert(time != -std::numeric_limits::max()); + return time; + }; + + while (!done) + { + double time = nextKeyTime(); + assert(time == 0 || (time / ticksPerSecond) > keyframe.time); + keyframe.time = time / ticksPerSecond; + + // Note the order of the logical-and, we don't want to short-circuit the function calls + done = parseAnimationChannel(pAiNode->mPositionKeys, pAiNode->mNumPositionKeys, time, pos, keyframe.translation); + done = parseAnimationChannel(pAiNode->mRotationKeys, pAiNode->mNumRotationKeys, time, rot, keyframe.rotation) && done; + done = parseAnimationChannel(pAiNode->mScalingKeys, pAiNode->mNumScalingKeys, time, scale, keyframe.scaling) && done; + for(auto c : channels) pAnimation->addKeyframe(c, keyframe); + } + } + + return pAnimation; + } + + bool createCamera(ImporterData& data) + { + if (data.pScene->mNumCameras == 0) return true; + if (data.pScene->mNumCameras > 1) + { + logWarning("Model file contains more then a single camera. Falcor only supports one camera per scene, we will use the first camera"); + } + + if (data.builder.hasCamera()) + { + logWarning("Found cameras in model file, but the scene already contains a camera. Ignoring the new camera"); + return true; + } + + const aiCamera* pAiCamera = data.pScene->mCameras[0]; + pAiCamera = pAiCamera; + Camera::SharedPtr pCamera = Camera::create(); + pCamera->setPosition(aiCast(pAiCamera->mPosition)); + pCamera->setUpVector(aiCast(pAiCamera->mUp)); + pCamera->setTarget(aiCast(pAiCamera->mLookAt) + aiCast(pAiCamera->mPosition)); + pCamera->setFocalLength(35);// fovYToFocalLength(pAiCamera->mHorizontalFOV * 2 / pAiCamera->mAspect, pCamera->getFrameHeight())); + pCamera->setAspectRatio(pAiCamera->mAspect); + pCamera->setDepthRange(pAiCamera->mClipPlaneNear, pAiCamera->mClipPlaneFar); + + size_t nodeID = data.getFalcorNode(pAiCamera->mName.C_Str(), 0); + + if(nodeID != SceneBuilder::kInvalidNode) + { + SceneBuilder::Node n; + n.name = "Camera.BaseMatrix"; + n.parent = nodeID; + n.transform = pCamera->getViewMatrix(); + nodeID = data.builder.addNode(n); + } + + // Find if the camera is affected by a node + data.builder.setCamera(pCamera, nodeID); + return true; + } + + bool addLightCommon(Light::ConstSharedPtrRef pLight, const mat4& baseMatrix, ImporterData& data, const aiLight* pAiLight) + { + pLight->setName(pAiLight->mName.C_Str()); + assert(pAiLight->mColorDiffuse == pAiLight->mColorSpecular); + pLight->setIntensity(aiCast(pAiLight->mColorSpecular)); + + // Find if the light is affected by a node + size_t nodeID = data.getFalcorNode(pAiLight->mName.C_Str(), 0); + if (nodeID != SceneBuilder::kInvalidNode) + { + SceneBuilder::Node n; + n.name = pLight->getName() + ".BaseMatrix"; + n.parent = nodeID; + n.transform = baseMatrix; + nodeID = data.builder.addNode(n); + } + data.builder.addLight(pLight, nodeID); + + return true; + } + + bool createDirLight(ImporterData& data, const aiLight* pAiLight) + { + DirectionalLight::SharedPtr pLight = DirectionalLight::create(); + vec3 direction = normalize(aiCast(pAiLight->mDirection)); + pLight->setWorldDirection(direction); + mat4 base; + base[2] = vec4(direction, 0); + return addLightCommon(pLight, base, data, pAiLight); + } + + bool createPointLight(ImporterData& data, const aiLight* pAiLight) + { + PointLight::SharedPtr pLight = PointLight::create(); + vec3 position = aiCast(pAiLight->mPosition); + vec3 lookAt = normalize(aiCast(pAiLight->mDirection)); + vec3 up = normalize(aiCast(pAiLight->mUp)); + pLight->setWorldPosition(position); + pLight->setWorldDirection(lookAt); + pLight->setOpeningAngle(pAiLight->mAngleOuterCone); + pLight->setPenumbraAngle(pAiLight->mAngleOuterCone - pAiLight->mAngleInnerCone); + + vec3 right = cross(up, lookAt); + mat4 base; + base[0] = vec4(right, 0); + base[1] = vec4(up, 0); + base[2] = vec4(lookAt, 0); + base[3] = vec4(position, 1); + + return addLightCommon(pLight, base, data, pAiLight); + } + + bool createLights(ImporterData& data) + { + for (uint32_t i = 0; i < data.pScene->mNumLights; i++) + { + const aiLight* pAiLight = data.pScene->mLights[i]; + switch (pAiLight->mType) + { + case aiLightSource_DIRECTIONAL: + if (!createDirLight(data, pAiLight)) return false; + break; + case aiLightSource_POINT: + case aiLightSource_SPOT: + if (!createPointLight(data, pAiLight)) return false; + break; + default: + logWarning("Unsupported ASSIMP light type " + to_string(pAiLight->mType)); + continue; + } + } + + return true; + } + + bool createAnimations(ImporterData& data) + { + for (uint32_t i = 0; i < data.pScene->mNumAnimations; i++) + { + Animation::SharedPtr pAnimation = createAnimation(data, data.pScene->mAnimations[i]); + data.builder.addAnimation(0, pAnimation); + } + return true; + } + + std::vector createTexCrdList(const aiVector3D* pAiTexCrd, uint32_t count) + { + std::vector v2(count); + for (uint32_t i = 0; i < count; i++) + { + assert(pAiTexCrd[i].z == 0); + v2[i] = vec2(pAiTexCrd[i].x, pAiTexCrd[i].y); + } + return v2; + } + + std::vector createIndexList(const aiMesh* pAiMesh) + { + const uint32_t perFaceIndexCount = pAiMesh->mFaces[0].mNumIndices; + const uint32_t indexCount = pAiMesh->mNumFaces * perFaceIndexCount; + + std::vector indices(indexCount); + + for (uint32_t i = 0; i < pAiMesh->mNumFaces; i++) + { + assert(pAiMesh->mFaces[i].mNumIndices == perFaceIndexCount); // Mesh contains mixed primitive types, can be solved using aiProcess_SortByPType + for (uint32_t j = 0; j < perFaceIndexCount; j++) + { + indices[i * perFaceIndexCount + j] = (uint32_t)(pAiMesh->mFaces[i].mIndices[j]); + } + } + return indices; + } + + void loadBones(const aiMesh* pAiMesh, const ImporterData& data, std::vector& weights, std::vector& ids) + { + const uint32_t vertexCount = pAiMesh->mNumVertices; + weights.resize(vertexCount); + ids.resize(vertexCount); + + for (uint32_t bone = 0; bone < pAiMesh->mNumBones; bone++) + { + const aiBone* pAiBone = pAiMesh->mBones[bone]; + assert(data.getNodeInstanceCount(pAiBone->mName.C_Str()) == 1); + size_t aiBoneID = data.getFalcorNode(pAiBone->mName.C_Str(), 0); + + // The way Assimp works, the weights holds the IDs of the vertices it affects. + // We loop over all the weights, initializing the vertices data along the way + for (uint32_t weightID = 0; weightID < pAiBone->mNumWeights; weightID++) + { + // Get the vertex the current weight affects + const aiVertexWeight& aiWeight = pAiBone->mWeights[weightID]; + + // Get the address of the Bone ID and weight for the current vertex + uvec4& vertexIds = ids[aiWeight.mVertexId]; + vec4& vertexWeights = weights[aiWeight.mVertexId]; + + // Find the next unused slot in the bone array of the vertex, and initialize it with the current value + bool emptySlotFound = false; + for (uint32_t j = 0; j < Scene::kMaxBonesPerVertex; j++) + { + if (vertexWeights[j] == 0) + { + vertexIds[j] = (uint32_t)aiBoneID; + vertexWeights[j] = aiWeight.mWeight; + emptySlotFound = true; + break; + } + } + + if (emptySlotFound == false) logError("One of the vertices has too many bones attached to it. If you'll continue, this bone will be ignored and the animation might not look correct"); + } + } + + // Now we need to normalize the weights for each vertex, since in some models the sum is larger than 1 + for (uint32_t i = 0; i < vertexCount; i++) + { + vec4& w = weights[i]; + float f = 0; + for (int j = 0; j < Scene::kMaxBonesPerVertex; j++) f += w[j]; + w /= f; + } + } + + bool createMeshes(ImporterData& data) + { + const aiScene* pScene = data.pScene; + for (uint32_t i = 0; i < pScene->mNumMeshes; i++) + { + SceneBuilder::Mesh mesh; + const aiMesh* pAiMesh = pScene->mMeshes[i]; + + // Indices + const auto indexList = createIndexList(pAiMesh); + mesh.indexCount = (uint32_t)indexList.size(); + mesh.pIndices = indexList.data(); + + // Vertices + assert(pAiMesh->mVertices); + mesh.vertexCount = pAiMesh->mNumVertices; + static_assert(sizeof(pAiMesh->mVertices[0]) == sizeof(mesh.pPositions[0])); + static_assert(sizeof(pAiMesh->mNormals[0]) == sizeof(mesh.pNormals[0])); + static_assert(sizeof(pAiMesh->mBitangents[0]) == sizeof(mesh.pBitangents[0])); + mesh.pPositions = (vec3*)pAiMesh->mVertices; + mesh.pNormals = (vec3*)pAiMesh->mNormals; + mesh.pBitangents = (vec3*)pAiMesh->mBitangents; + const auto& texCrd = pAiMesh->HasTextureCoords(0) ? createTexCrdList(pAiMesh->mTextureCoords[0], pAiMesh->mNumVertices) : std::vector(); + mesh.pTexCrd = texCrd.size() ? texCrd.data() : nullptr; + + std::vector boneIds; + std::vector boneWeights; + + if (pAiMesh->HasBones()) + { + loadBones(pAiMesh, data, boneWeights, boneIds); + mesh.pBoneIDs = boneIds.data(); + mesh.pBoneWeights = boneWeights.data(); + } + + switch (pAiMesh->mFaces[0].mNumIndices) + { + case 1: mesh.topology = Vao::Topology::PointList; break; + case 2: mesh.topology = Vao::Topology::LineList; break; + case 3: mesh.topology = Vao::Topology::TriangleList; break; + default: + logError(std::string("Error when creating mesh. Unknown topology with " + std::to_string(pAiMesh->mFaces[0].mNumIndices) + " indices.")); + should_not_get_here(); + } + + mesh.pMaterial = data.materialMap.at(pAiMesh->mMaterialIndex); + assert(mesh.pMaterial); + size_t meshID = data.builder.addMesh(mesh); + if (meshID == SceneBuilder::kInvalidNode) return false; + data.meshMap[i] = meshID; + } + + return true; + } + + bool isBone(ImporterData& data, const std::string& name) + { + return data.localToBindPoseMatrices.find(name) != data.localToBindPoseMatrices.end(); + } + + std::string getNodeType(ImporterData& data, const aiNode* pNode) + { + if (pNode->mNumMeshes > 0) return "mesh instance"; + if (isBone(data, pNode->mName.C_Str())) return "bone"; + else return "local transform"; + } + + void dumpSceneGraphHierarchy(ImporterData& data, const std::string& filename, aiNode* pRoot) + { + std::ofstream dotfile; + dotfile.open(filename.c_str()); + + std::function dumpNode = [&dotfile, &dumpNode, &data](const aiNode* pNode) + { + for (uint32_t i = 0; i < pNode->mNumChildren; i++) + { + const aiNode* pChild = pNode->mChildren[i]; + std::string parent = pNode->mName.C_Str(); + std::string parentType = getNodeType(data, pNode); + std::string me = pChild->mName.C_Str(); + std::string myType = getNodeType(data, pChild); + std::replace(parent.begin(), parent.end(), '.', '_'); + std::replace(me.begin(), me.end(), '.', '_'); + std::replace(parent.begin(), parent.end(), '$', '_'); + std::replace(me.begin(), me.end(), '$', '_'); + + dotfile << parent << " (" << parentType << ") " << " -> " << me << " (" << myType << ") " << std::endl; + + dumpNode(pChild); + } + }; + + // Header + dotfile << "digraph SceneGraph {" << std::endl; + dumpNode(pRoot); + // Close the file + dotfile << "}" << std::endl; // closing graph scope + dotfile.close(); + } + + mat4 getLocalToBindPoseMatrix(ImporterData& data, const std::string& name) + { + return isBone(data, name) ? data.localToBindPoseMatrices[name] : identity(); + } + + bool parseNode(ImporterData& data, const aiNode* pCurrent, bool hasBoneAncestor) + { + SceneBuilder::Node n; + n.name = pCurrent->mName.C_Str(); + bool currentIsBone = isBone(data, n.name); + assert(currentIsBone == false || pCurrent->mNumMeshes == 0); + + n.parent = pCurrent->mParent ? data.getFalcorNode(pCurrent->mParent) : SceneBuilder::kInvalidNode; + n.transform = aiCast(pCurrent->mTransformation); + n.localToBindPose = getLocalToBindPoseMatrix(data, n.name); + + data.addAiNode(pCurrent, data.builder.addNode(n)); + + bool b = true; + // visit the children + for (uint32_t i = 0; i < pCurrent->mNumChildren; i++) + { + b |= parseNode(data, pCurrent->mChildren[i], currentIsBone || hasBoneAncestor); + } + return b; + } + + void createBoneList(ImporterData& data) + { + const aiScene* pScene = data.pScene; + auto& boneMatrices = data.localToBindPoseMatrices; + + for (uint32_t meshID = 0; meshID < pScene->mNumMeshes; meshID++) + { + const aiMesh* pMesh = pScene->mMeshes[meshID]; + if (pMesh->HasBones() == false) continue;; + for (uint32_t boneID = 0; boneID < pMesh->mNumBones; boneID++) + { + boneMatrices[pMesh->mBones[boneID]->mName.C_Str()] = aiCast(pMesh->mBones[boneID]->mOffsetMatrix); + } + } + } + + bool createSceneGraph(ImporterData& data) + { + createBoneList(data); + aiNode* pRoot = data.pScene->mRootNode; + assert(isBone(data, pRoot->mName.C_Str()) == false); + bool success = parseNode(data, pRoot, false); +// dumpSceneGraphHierarchy(data, "graph.dotfile", pRoot); + return success; + } + + bool addMeshes(ImporterData& data, aiNode* pNode) + { + size_t nodeID = data.getFalcorNode(pNode); + for (size_t mesh = 0; mesh < pNode->mNumMeshes; mesh++) + { + size_t meshID = data.meshMap.at(pNode->mMeshes[mesh]); + + if (data.modelInstances.size()) + { + for(size_t instance = 0 ; instance < data.modelInstances.size() ; instance++) + { + size_t instanceNode = nodeID; + if(data.modelInstances[instance] != mat4()) + { + // Add nodes + SceneBuilder::Node n; + n.name = "Node" + to_string(nodeID) + ".instance" + to_string(instance); + n.parent = nodeID; + n.transform = data.modelInstances[instance]; + instanceNode = data.builder.addNode(n); + } + data.builder.addMeshInstance(instanceNode, meshID); + } + } + else data.builder.addMeshInstance(nodeID, meshID); + } + + bool b = true; + // visit the children + for (uint32_t i = 0; i < pNode->mNumChildren; i++) + { + b |= addMeshes(data, pNode->mChildren[i]); + } + return b; + } + + void loadTextures(ImporterData& data, const aiMaterial* pAiMaterial, const std::string& folder, Material* pMaterial, ImportMode importMode, bool useSrgb) + { + for (int i = 0; i < AI_TEXTURE_TYPE_MAX; ++i) + { + aiTextureType aiType = (aiTextureType)i; + + uint32_t textureCount = pAiMaterial->GetTextureCount(aiType); + + if (textureCount >= 1) + { + if (textureCount != 1) + { + logError("Can't create material with more then one texture per Type"); + return; + } + + // Get the texture name + aiString path; + pAiMaterial->GetTexture(aiType, 0, &path); + std::string s(path.data); + Texture::SharedPtr pTex = nullptr; + + if (s.empty()) + { + logWarning("Texture has empty file name, ignoring."); + continue; + } + + // Check if the texture was already loaded + const auto& a = data.textureCache.find(s); + if (a != data.textureCache.end()) + { + pTex = a->second; + } + else + { + // create a new texture + std::string fullpath = folder + '/' + s; + fullpath = replaceSubstring(fullpath, "\\", "/"); + pTex = Texture::createFromFile(fullpath, true, isSrgbRequired(aiType, useSrgb, pMaterial->getShadingModel())); + if (pTex) + { + data.textureCache[s] = pTex; + } + } + + assert(pTex != nullptr); + setTexture(aiType, importMode, pMaterial, pTex); + } + } + + // Flush upload heap after every material so we don't accumulate a ton of memory usage when loading a model with a lot of textures + gpDevice->flushAndSync(); + } + + Material::SharedPtr createMaterial(ImporterData& data, const aiMaterial* pAiMaterial, const std::string& folder, ImportMode importMode, bool useSrgb) + { + aiString name; + pAiMaterial->Get(AI_MATKEY_NAME, name); + + // Parse the name + std::string nameStr = std::string(name.C_Str()); + if (nameStr.empty()) + { + logWarning("Material with no name found -> renaming to `unnamed`"); + nameStr = "unnamed"; + } + auto nameVec = splitString(nameStr, "."); // The name might contain information about the material + Material::SharedPtr pMaterial = Material::create(nameVec[0]); + + // Determine shading model. + // MetalRough is the default for everything except OBJ. Check that both flags aren't set simultaneously. + SceneBuilder::Flags builderFlags = data.builder.getFlags(); + assert(!(is_set(builderFlags, SceneBuilder::Flags::UseSpecGlossMaterials) && is_set(builderFlags, SceneBuilder::Flags::UseMetalRoughMaterials))); + if (is_set(builderFlags, SceneBuilder::Flags::UseSpecGlossMaterials) || (importMode == ImportMode::OBJ && !is_set(builderFlags, SceneBuilder::Flags::UseMetalRoughMaterials))) + { + pMaterial->setShadingModel(ShadingModelSpecGloss); + } + + // Load textures. Note that loading is affected by the current shading model. + loadTextures(data, pAiMaterial, folder, pMaterial.get(), importMode, useSrgb); + + // Opacity + float opacity; + if (pAiMaterial->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) + { + vec4 diffuse = pMaterial->getBaseColor(); + diffuse.a = opacity; + pMaterial->setBaseColor(diffuse); + } + + // Bump scaling + float bumpScaling; + if (pAiMaterial->Get(AI_MATKEY_BUMPSCALING, bumpScaling) == AI_SUCCESS) + { + float bumpOffset = pMaterial->getHeightOffset(); + pMaterial->setHeightScaleOffset(bumpScaling, bumpOffset); + } + + // Shininess + float shininess; + if (pAiMaterial->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) + { + // Convert OBJ/MTL Phong exponent to glossiness. + if (importMode == ImportMode::OBJ) + { + float roughness = convertShininessToRoughness(shininess); + shininess = 1.f - roughness; + } + vec4 spec = pMaterial->getSpecularParams(); + spec.a = shininess; + pMaterial->setSpecularParams(spec); + } + + // Refraction + float refraction; + if (pAiMaterial->Get(AI_MATKEY_REFRACTI, refraction) == AI_SUCCESS) pMaterial->setIndexOfRefraction(refraction); + + // Diffuse color + aiColor3D color; + if (pAiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) + { + vec4 diffuse = vec4(color.r, color.g, color.b, pMaterial->getBaseColor().a); + pMaterial->setBaseColor(diffuse); + } + + // Specular color + if (pAiMaterial->Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) + { + vec4 specular = vec4(color.r, color.g, color.b, pMaterial->getSpecularParams().a); + pMaterial->setSpecularParams(specular); + } + + // Emissive color + if (pAiMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) + { + vec3 emissive = vec3(color.r, color.g, color.b); + pMaterial->setEmissiveColor(emissive); + } + + // Double-Sided + int isDoubleSided; + if (pAiMaterial->Get(AI_MATKEY_TWOSIDED, isDoubleSided) == AI_SUCCESS) + { + pMaterial->setDoubleSided((isDoubleSided != 0)); + } + + // Handle GLTF PBR materials + if (importMode == ImportMode::GLTF2) + { + if (pAiMaterial->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, color) == AI_SUCCESS) + { + vec4 baseColor = vec4(color.r, color.g, color.b, pMaterial->getBaseColor().a); + pMaterial->setBaseColor(baseColor); + } + + vec4 specularParams = pMaterial->getSpecularParams(); + + float metallic; + if (pAiMaterial->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, metallic) == AI_SUCCESS) + { + specularParams.b = metallic; + } + + float roughness; + if (pAiMaterial->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, roughness) == AI_SUCCESS) + { + specularParams.g = roughness; + } + + pMaterial->setSpecularParams(specularParams); + } + + // Parse the information contained in the name + // The first part is the material name, the other parts provides some info about the material properties + if (nameVec.size() > 1) + { + for (size_t i = 1; i < nameVec.size(); i++) + { + std::string str = nameVec[i]; + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if (str == "doublesided") pMaterial->setDoubleSided(true); + else logWarning("Unknown material property found in the material's name - `" + nameVec[i] + "`"); + } + } + return pMaterial; + } + + bool createAllMaterials(ImporterData& data, const std::string& modelFolder, ImportMode importMode) + { + bool useSrgb = !is_set(data.builder.getFlags(), SceneBuilder::Flags::AssumeLinearSpaceTextures); + + for (uint32_t i = 0; i < data.pScene->mNumMaterials; i++) + { + const aiMaterial* pAiMaterial = data.pScene->mMaterials[i]; + auto pMaterial = createMaterial(data, pAiMaterial, modelFolder, importMode, useSrgb); + if (pMaterial == nullptr) + { + logError("Can't allocate memory for material"); + return false; + } + data.materialMap[i] = pMaterial; + } + + return true; + } + + BoneMeshMap createBoneMap(const aiScene* pScene) + { + BoneMeshMap boneMap; + + for (uint32_t meshID = 0; meshID < pScene->mNumMeshes; meshID++) + { + const aiMesh* pMesh = pScene->mMeshes[meshID]; + for (uint32_t boneID = 0; boneID < pMesh->mNumBones; boneID++) + { + try + { + boneMap.at(pMesh->mBones[boneID]->mName.C_Str()).push_back(meshID); + } + catch (std::out_of_range e) + { + std::vector meshIDs; + meshIDs.push_back(meshID); + boneMap[pMesh->mBones[boneID]->mName.C_Str()] = meshIDs; + } + } + } + + return boneMap; + } + + MeshInstanceList countMeshInstances(const aiScene* pScene) + { + MeshInstanceList meshInstances(pScene->mNumMeshes); + + std::function countNodeMeshs = [&](const aiNode* pNode) + { + for (uint32_t i = 0; i < pNode->mNumMeshes; i++) + { + meshInstances[pNode->mMeshes[i]].push_back(pNode); + } + + for (uint32_t i = 0; i < pNode->mNumChildren; i++) + { + countNodeMeshs(pNode->mChildren[i]); + } + }; + countNodeMeshs(pScene->mRootNode); + + return meshInstances; + } + + bool validateBones(const aiScene* pScene) + { + // Make sure that each bone is only affecting a single mesh. + // Our skinning system depends on that, because we apply the inverse world transformation to blended vertices. We do that because apparently ASSIMP's bone matrices are pre-multiplied with the final world transform + // which results in the world-space blended-vertices, but we'd like them to be in local-space + BoneMeshMap boneMap = createBoneMap(pScene); + MeshInstanceList meshInstances = countMeshInstances(pScene); + + for (auto& b : boneMap) + { + for (uint32_t i = 0; i < b.second.size(); i++) + { + if (meshInstances[b.second[i]].size() != 1) + { + logError(b.first + " references a mesh with multiple instances"); + return false; + } + + if (i > 0 && meshInstances[b.second[i]][0]->mTransformation != meshInstances[b.second[i - 1]][0]->mTransformation) + { + logError(b.first + " is contained within mesh instances with different world transform matrices"); + return false; + } + } + } + + return true; + } + + void verifyScene(const aiScene* pScene) + { + bool b = true; + + // No internal textures + if (pScene->mTextures != 0) + { + b = false; + logWarning("Model has internal textures which Falcor doesn't support"); + } + + b = validateBones(pScene); + assert(b); + } + } + + bool AssimpImporter::import(const std::string& filename, SceneBuilder& builder, const InstanceMatrices& meshInstances) + { + std::string fullpath; + if (findFileInDataDirectories(filename, fullpath) == false) + { + logError(std::string("Can't find model file ") + filename); + return false; + } + + SceneBuilder::Flags builderFlags = builder.getFlags(); + uint32_t assimpFlags = aiProcessPreset_TargetRealtime_MaxQuality | + aiProcess_FlipUVs | + 0; + + assimpFlags &= ~(aiProcess_CalcTangentSpace); // Never use Assimp's tangent gen code + assimpFlags &= ~(aiProcess_FindDegenerates); // Avoid converting degenerated triangles to lines + assimpFlags &= ~(aiProcess_OptimizeGraph); // Never use as it doesn't handle transforms with negative determinants + if (is_set(builderFlags, SceneBuilder::Flags::DontMergeMeshes)) assimpFlags &= ~aiProcess_OptimizeMeshes; // Avoid merging original meshes + + Assimp::Importer importer; + const aiScene* pScene = importer.ReadFile(fullpath, assimpFlags); + + if (pScene == nullptr) + { + std::string str("Can't open model file '"); + str = str + std::string(filename) + "'\n" + importer.GetErrorString(); + logError(str); + return false; + } + + verifyScene(pScene); + + // Extract the folder name + auto last = fullpath.find_last_of("/\\"); + std::string modelFolder = fullpath.substr(0, last); + + ImporterData data(pScene, builder, meshInstances); + + // Enable special treatment for obj and gltf files + ImportMode importMode = ImportMode::Default; + if (hasSuffix(filename, ".obj", false)) importMode = ImportMode::OBJ; + if (hasSuffix(filename, ".gltf", false) || hasSuffix(filename, ".glb", false)) importMode = ImportMode::GLTF2; + + if (createAllMaterials(data, modelFolder, importMode) == false) + { + logError(std::string("Can't create materials for model ") + filename); + return false; + } + + if (createSceneGraph(data) == false) + { + logError(std::string("Can't create draw lists for model ") + filename); + return false; + } + + if (createMeshes(data) == false) + { + logError(std::string("Can't create meshes for model ") + filename); + return false; + } + + if (addMeshes(data, data.pScene->mRootNode) == false) + { + logError(std::string("Can't add meshes for model ") + filename); + return false; + } + + if (createAnimations(data) == false) + { + logError(std::string("Can't create animations for model ") + filename); + return false; + } + + if (createCamera(data) == false) + { + logError(std::string("Can't create a camera for model ") + filename); + return false; + } + + if (createLights(data) == false) + { + logError(std::string("Can't create a lights for model ") + filename); + return false; + } + return true; + } + + bool AssimpImporter::import(const std::string& filename, SceneBuilder& builder) + { + InstanceMatrices meshInstances(1); + return import(filename, builder, meshInstances); + } +} diff --git a/Source/Falcor/Scene/Importers/AssimpImporter.h b/Source/Falcor/Scene/Importers/AssimpImporter.h new file mode 100644 index 000000000..4192a96e4 --- /dev/null +++ b/Source/Falcor/Scene/Importers/AssimpImporter.h @@ -0,0 +1,45 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Scene/SceneBuilder.h" + +namespace Falcor +{ + class dlldecl AssimpImporter + { + public: + using InstanceMatrices = SceneBuilder::InstanceMatrices; + + static bool import(const std::string& filename, SceneBuilder& builder); + static bool import(const std::string& filename, SceneBuilder& builder, const InstanceMatrices& meshInstances); + private: + AssimpImporter() = default; + AssimpImporter(const AssimpImporter&) = delete; + void operator=(const AssimpImporter&) = delete; + }; +} diff --git a/Framework/Source/Graphics/Scene/SceneImporter.cpp b/Source/Falcor/Scene/Importers/SceneImporter.cpp similarity index 55% rename from Framework/Source/Graphics/Scene/SceneImporter.cpp rename to Source/Falcor/Scene/Importers/SceneImporter.cpp index b900a0603..73964c995 100644 --- a/Framework/Source/Graphics/Scene/SceneImporter.cpp +++ b/Source/Falcor/Scene/Importers/SceneImporter.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,24 +25,144 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "SceneImporter.h" +#include "rapidjson/document.h" #include "rapidjson/error/en.h" -#include "Scene.h" -#include "Utils/Platform/OS.h" -#include -#include -#include -#include "Graphics/TextureHelper.h" -#include "API/Device.h" -#include "Data/HostDeviceSharedMacros.h" - -#define SCENE_IMPORTER -#include "SceneExportImportCommon.h" +#include "Core/API/Device.h" +#include "glm/gtx/euler_angles.hpp" +#include "glm/gtx/transform.hpp" +#include namespace Falcor { - bool SceneImporter::error(const std::string& msg) + namespace SceneKeys + { + + static const char* kInclude = "include"; + + // Not supported in exporter yet + static const char* kLightProbes = "light_probes"; + static const char* kLightProbeRadius = "radius"; + static const char* kLightProbeDiffSamples = "diff_samples"; + static const char* kLightProbeSpecSamples = "spec_samples"; + + // Keys for values in older scene versions that are not exported anymore + static const char* kCamFovY = "fovY"; + static const char* kActivePath = "active_path"; + static const char* kAmbientIntensity = "ambient_intensity"; + static const char* kVersion = "version"; + static const char* kSceneUnit = "scene_unit"; + static const char* kCameraSpeed = "camera_speed"; + static const char* kActiveCamera = "active_camera"; + static const char* kLightingScale = "lighting_scale"; + + static const char* kName = "name"; + static const char* kEnvMap = "env_map"; + + static const char* kModels = "models"; + static const char* kFilename = "file"; + static const char* kModelInstances = "instances"; + static const char* kTranslationVec = "translation"; + static const char* kRotationVec = "rotation"; + static const char* kScalingVec = "scaling"; + static const char* kActiveAnimation = "active_animation"; + + static const char* kCameras = "cameras"; + static const char* kCamPosition = "pos"; + static const char* kCamTarget = "target"; + static const char* kCamUp = "up"; + static const char* kCamFocalLength = "focal_length"; + static const char* kCamDepthRange = "depth_range"; + static const char* kCamAspectRatio = "aspect_ratio"; + static const char* kCamFocalDistance = "focal_distance"; + static const char* kCamApertureRadius = "aperture_radius"; + static const char* kCamSpeed = "speed"; + + static const char* kLights = "lights"; + static const char* kType = "type"; + static const char* kDirLight = "dir_light"; + static const char* kPointLight = "point_light"; + static const char* kAreaLightRect = "area_light_rect"; + static const char* kAreaLightSphere = "area_light_sphere"; + static const char* kAreaLightDisc = "area_light_disc"; + static const char* kLightIntensity = "intensity"; + static const char* kLightOpeningAngle = "opening_angle"; + static const char* kLightPenumbraAngle = "penumbra_angle"; + static const char* kLightPos = "pos"; + static const char* kLightDirection = "direction"; + + static const char* kPaths = "paths"; + static const char* kAttachedObjects = "attached_objects"; + static const char* kModelInstance = "model_instance"; + static const char* kLight = "light"; + static const char* kCamera = "camera"; + + static const char* kMaterial = "material"; + static const char* kShadingModel = "shading_model"; + static const char* kShadingSpecGloss = "spec_gloss"; + static const char* kShadingMetalRough = "metal_rough"; + + static const char* kUserDefined = "user_defined"; + }; + + class SceneImporterImpl + { + public: + SceneImporterImpl(SceneBuilder& builder) : mBuilder(builder) {} + bool load(const std::string& filename); + + private: + bool parseVersion(const rapidjson::Value& jsonVal); + bool parseSceneUnit(const rapidjson::Value& jsonVal); + bool parseModels(const rapidjson::Value& jsonVal); + bool parseLights(const rapidjson::Value& jsonVal); + bool parseLightProbes(const rapidjson::Value& jsonVal); + bool parseCameras(const rapidjson::Value& jsonVal); + bool parseCamera(const rapidjson::Value& jsonVal); + bool parseAmbientIntensity(const rapidjson::Value& jsonVal); + bool parseActiveCamera(const rapidjson::Value& jsonVal); + bool parseCameraSpeed(const rapidjson::Value& jsonVal); + bool parseLightingScale(const rapidjson::Value& jsonVal); + bool parsePaths(const rapidjson::Value& jsonVal); + bool parseUserDefinedSection(const rapidjson::Value& jsonVal); + bool parseActivePath(const rapidjson::Value& jsonVal); + bool parseIncludes(const rapidjson::Value& jsonVal); + bool parseEnvMap(const rapidjson::Value& jsonVal); + + bool topLevelLoop(); + + bool loadIncludeFile(const std::string& Include); + + std::vector parseModelInstances(const rapidjson::Value& jsonVal); + bool createModel(const rapidjson::Value& jsonModel); + bool createPointLight(const rapidjson::Value& jsonLight); + bool createDirLight(const rapidjson::Value& jsonLight); + bool createAnalyticAreaLight(const rapidjson::Value& jsonLight); + + bool error(const std::string& msg); + + template + bool getFloatVec(const rapidjson::Value& jsonVal, const std::string& desc, float vec[VecSize]); + bool getFloatVecAnySize(const rapidjson::Value& jsonVal, const std::string& desc, std::vector& vec); + rapidjson::Document mJDoc; + SceneBuilder& mBuilder; + std::string mFilename; + std::string mDirectory; + + using LightMap = std::map; + + struct FuncValue + { + const std::string token; + decltype(&SceneImporterImpl::parseModels) func; + }; + + static const FuncValue kFunctionTable[]; + bool validateSceneFile(); + }; + + bool SceneImporterImpl::error(const std::string& msg) { std::string err = "Error when parsing scene file \"" + mFilename + "\".\n" + msg; #if _LOG_ENABLED @@ -54,7 +174,7 @@ namespace Falcor } template - bool SceneImporter::getFloatVec(const rapidjson::Value& jsonVal, const std::string& desc, float vec[VecSize]) + bool SceneImporterImpl::getFloatVec(const rapidjson::Value& jsonVal, const std::string& desc, float vec[VecSize]) { if (jsonVal.IsArray() == false) { @@ -79,7 +199,7 @@ namespace Falcor return true; } - bool SceneImporter::getFloatVecAnySize(const rapidjson::Value& jsonVal, const std::string& desc, std::vector& vec) + bool SceneImporterImpl::getFloatVecAnySize(const rapidjson::Value& jsonVal, const std::string& desc, std::vector& vec) { if (jsonVal.IsArray() == false) { @@ -99,83 +219,61 @@ namespace Falcor return true; } - bool SceneImporter::loadScene(Scene& scene, const std::string& filename, Model::LoadFlags modelLoadFlags, Scene::LoadFlags sceneLoadFlags) + std::vector SceneImporterImpl::parseModelInstances(const rapidjson::Value& jsonVal) { - SceneImporter importer(scene); - return importer.load(filename, modelLoadFlags, sceneLoadFlags); - } + struct ModelInstance + { + vec3 scaling = vec3(1, 1, 1); + vec3 position = vec3(0, 0, 0); + vec3 rotation = vec3(0, 0, 0); + }; + + std::vector instances; - bool SceneImporter::createModelInstances(const rapidjson::Value& jsonVal, const Model::SharedPtr& pModel) - { if (jsonVal.IsArray() == false) { - return error("Model instances should be an array of objects"); + logError("Model instances should be an array of objects"); + return {}; } for (uint32_t i = 0; i < jsonVal.Size(); i++) { - const auto& instance = jsonVal[i]; - glm::vec3 scaling(1, 1, 1); - glm::vec3 translation(0, 0, 0); - glm::vec3 rotation(0, 0, 0); - std::string name = "Instance " + std::to_string(i); + const auto& jsonInstance = jsonVal[i]; + ModelInstance instance; - for (auto m = instance.MemberBegin(); m < instance.MemberEnd(); m++) + for (auto m = jsonInstance.MemberBegin(); m < jsonInstance.MemberEnd(); m++) { std::string key(m->name.GetString()); - if (key == SceneKeys::kName) - { - if (m->value.IsString() == false) - { - return error("Model instance name should be a string value."); - } - name = std::string(m->value.GetString()); - } - else if (key == SceneKeys::kTranslationVec) - { - if (getFloatVec<3>(m->value, "Model instance translation vector", &translation[0]) == false) - { - return false; - } - } - else if (key == SceneKeys::kScalingVec) - { - if (getFloatVec<3>(m->value, "Model instance scale vector", &scaling[0]) == false) - { - return false; - } - } + if (key == SceneKeys::kName) continue; + else if (key == SceneKeys::kTranslationVec) getFloatVec<3>(m->value, "Model instance translation vector", &instance.position[0]); + else if (key == SceneKeys::kScalingVec) getFloatVec<3>(m->value, "Model instance scale vector", &instance.scaling[0]); else if (key == SceneKeys::kRotationVec) { - if (getFloatVec<3>(m->value, "Model instance rotation vector", &rotation[0]) == false) + if (getFloatVec<3>(m->value, "Model instance rotation vector", &instance.rotation[0])) { - return false; + instance.rotation = glm::radians(instance.rotation); } - - rotation = glm::radians(rotation); - } - else - { - return error("Unknown key \"" + key + "\" when parsing model instance"); } + else logError("Unknown key \"" + key + "\" when parsing model instance"); } - if (isNameDuplicate(name, mInstanceMap, "model instances")) - { - return false; - } - else - { - auto pInstance = Scene::ModelInstance::create(pModel, translation, rotation, scaling, name); - mInstanceMap[pInstance->getName()] = pInstance; - mScene.addModelInstance(pInstance); - } + instances.push_back(instance); } - return true; + std::vector matrices(instances.size()); + for (size_t i = 0; i < matrices.size(); i++) + { + mat4 T; + T[3] = vec4(instances[i].position, 1); + mat4 S = scale(instances[i].scaling); + mat4 R = yawPitchRoll(instances[i].rotation[0], instances[i].rotation[1], instances[i].rotation[2]); + matrices[i] = T * R * S; + } + + return matrices; } - bool SceneImporter::createModel(const rapidjson::Value& jsonModel) + bool SceneImporterImpl::createModel(const rapidjson::Value& jsonModel) { // Model must have at least a filename if (jsonModel.HasMember(SceneKeys::kFilename) == false) @@ -197,7 +295,7 @@ namespace Falcor } // Parse additional properties that affect loading - Model::LoadFlags modelFlags = mModelLoadFlags; + SceneBuilder::Flags buildFlags = mBuilder.getFlags(); if (jsonModel.HasMember(SceneKeys::kMaterial)) { const auto& materialSettings = jsonModel[SceneKeys::kMaterial]; @@ -212,11 +310,11 @@ namespace Falcor { if (m->value == SceneKeys::kShadingSpecGloss) { - modelFlags |= Model::LoadFlags::UseSpecGlossMaterials; + buildFlags |= SceneBuilder::Flags::UseSpecGlossMaterials; } else if (m->value == SceneKeys::kShadingMetalRough) { - modelFlags |= Model::LoadFlags::UseMetalRoughMaterials; + buildFlags |= SceneBuilder::Flags::UseMetalRoughMaterials; } else { @@ -226,14 +324,7 @@ namespace Falcor } } - // Load the model - auto pModel = Model::createFromFile(file.c_str(), modelFlags); - if (pModel == nullptr) - { - return error("Could not load model: " + file); - } - - bool instanceAdded = false; + std::vector instances; // Loop over the other members for (auto jval = jsonModel.MemberBegin(); jval != jsonModel.MemberEnd(); jval++) @@ -249,34 +340,29 @@ namespace Falcor { return error("Model name should be a string value."); } - pModel->setName(std::string(jval->value.GetString())); } else if (keyName == SceneKeys::kModelInstances) { - if (createModelInstances(jval->value, pModel) == false) - { - return false; - } - - instanceAdded = true; + instances = parseModelInstances(jval->value); } else if (keyName == SceneKeys::kActiveAnimation) { - if (jval->value.IsUint() == false) - { - return error("Model active animation should be an unsigned integer"); - } - uint32_t activeAnimation = jval->value.GetUint(); - if (activeAnimation >= pModel->getAnimationsCount()) - { - std::string msg = "Warning when parsing scene file \"" + mFilename + "\".\nModel " + pModel->getName() + " was specified with active animation " + std::to_string(activeAnimation); - msg += ", but model only has " + std::to_string(pModel->getAnimationsCount()) + " animations. Ignoring field"; - logWarning(msg); - } - else - { - pModel->setActiveAnimation(activeAnimation); - } +// #SCENEV2 +// if (jval->value.IsUint() == false) +// { +// return error("Model active animation should be an unsigned integer"); +// } +// uint32_t activeAnimation = jval->value.GetUint(); +// if (activeAnimation >= pModel->getAnimationsCount()) +// { +// std::string msg = "Warning when parsing scene file \"" + mFilename + "\".\nModel " + pModel->getName() + " was specified with active animation " + std::to_string(activeAnimation); +// msg += ", but model only has " + std::to_string(pModel->getAnimationsCount()) + " animations. Ignoring field"; +// logWarning(msg); +// } +// else +// { +// pModel->setActiveAnimation(activeAnimation); +// } } else if (keyName == SceneKeys::kMaterial) { @@ -288,16 +374,13 @@ namespace Falcor } } - // If no instances for the model were loaded from the scene file - if (instanceAdded == false) - { - mScene.addModelInstance(pModel, "Instance 0"); - } + assert(std::filesystem::path(file).extension() != ".fscene"); // #SCENE this will cause an endless recursion. We may want to fix it + mBuilder.import(file.c_str(), instances); return true; } - bool SceneImporter::parseModels(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseModels(const rapidjson::Value& jsonVal) { if (jsonVal.IsArray() == false) { @@ -315,7 +398,7 @@ namespace Falcor return true; } - bool SceneImporter::createDirLight(const rapidjson::Value& jsonLight) + bool SceneImporterImpl::createDirLight(const rapidjson::Value& jsonLight) { auto pDirLight = DirectionalLight::create(); @@ -327,12 +410,12 @@ namespace Falcor { if (value.IsString() == false) { - return error("Point light name should be a string"); + return error("Directional light name should be a string"); } std::string name = value.GetString(); if (name.find(' ') != std::string::npos) { - return error("Point light name can't have spaces"); + return error("Directional light name can't have spaces"); } pDirLight->setName(name); } @@ -363,11 +446,12 @@ namespace Falcor return error("Invalid key found in directional light object. Key == " + key + "."); } } - mScene.addLight(pDirLight); + + mBuilder.addLight(pDirLight); return true; } - bool SceneImporter::createPointLight(const rapidjson::Value& jsonLight) + bool SceneImporterImpl::createPointLight(const rapidjson::Value& jsonLight) { auto pPointLight = PointLight::create(); @@ -379,12 +463,12 @@ namespace Falcor { if (value.IsString() == false) { - return error("Dir light name should be a string"); + return error("Point light name should be a string"); } std::string name = value.GetString(); if (name.find(' ') != std::string::npos) { - return error("Dir light name can't have spaces"); + return error("Point light name can't have spaces"); } pPointLight->setName(name); } @@ -396,7 +480,7 @@ namespace Falcor { if (value.IsNumber() == false) { - return error("Camera's FOV should be a number"); + return error("Point light's FOV should be a number"); } float angle = (float)value.GetDouble(); // Convert to radiance @@ -407,7 +491,7 @@ namespace Falcor { if (value.IsNumber() == false) { - return error("Camera's FOV should be a number"); + return error("Point light's FOV should be a number"); } float angle = (float)value.GetDouble(); // Convert to radiance @@ -447,23 +531,26 @@ namespace Falcor } } - if (isNameDuplicate(pPointLight->getName(), mLightMap, "lights")) - { - return false; - } - else - { - mLightMap[pPointLight->getName()] = pPointLight; - mScene.addLight(pPointLight); - } + mBuilder.addLight(pPointLight); return true; } // Support for analytic area lights - bool SceneImporter::createAnalyticAreaLight(const rapidjson::Value& jsonLight) + bool SceneImporterImpl::createAnalyticAreaLight(const rapidjson::Value& jsonLight) { - auto pAreaLight = AnalyticAreaLight::create(); + // Get the type of area light. + auto typeKey = jsonLight.FindMember(SceneKeys::kType); + if (typeKey == jsonLight.MemberEnd() || !typeKey->value.IsString()) error("Area light missing/invalid '" + std::string(SceneKeys::kType) + "' key"); + + uint32_t type; + if (typeKey->value.GetString() == SceneKeys::kAreaLightRect) type = LightAreaRect; + else if (typeKey->value.GetString() == SceneKeys::kAreaLightSphere) type = LightAreaSphere; + else if (typeKey->value.GetString() == SceneKeys::kAreaLightDisc) type = LightAreaDisc; + else return error("Invalid area light type"); + + // Create the light. + auto pAreaLight = AnalyticAreaLight::create(type); glm::vec3 scaling(1, 1, 1); glm::vec3 translation(0, 0, 0); @@ -488,16 +575,7 @@ namespace Falcor } else if (key == SceneKeys::kType) { - if (value.IsString() == false) - { - return error("Area light type should be a string"); - } - - std::string type = value.GetString(); - if (type == SceneKeys::kAreaLightRect) pAreaLight->setType(LightAreaRect); - else if (type == SceneKeys::kAreaLightSphere) pAreaLight->setType(LightAreaSphere); - else if (type == SceneKeys::kAreaLightDisc) pAreaLight->setType(LightAreaDisc); - else return error("Invalid area light type"); + // Already parsed } else if (key == SceneKeys::kLightIntensity) { @@ -545,19 +623,11 @@ namespace Falcor glm::mat4 composite = translationMtx * rotationMtx; pAreaLight->setTransformMatrix(composite); - if (isNameDuplicate(pAreaLight->getName(), mLightMap, "lights")) - { - return false; - } - else - { - mLightMap[pAreaLight->getName()] = pAreaLight; - mScene.addLight(pAreaLight); - } + mBuilder.addLight(pAreaLight); return true; } - bool SceneImporter::parseLights(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseLights(const rapidjson::Value& jsonVal) { if (jsonVal.IsArray() == false) { @@ -607,7 +677,7 @@ namespace Falcor return true; } - bool SceneImporter::parseLightProbes(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseLightProbes(const rapidjson::Value& jsonVal) { if (jsonVal.IsArray() == false) { @@ -687,176 +757,30 @@ namespace Falcor LightProbe::SharedPtr pLightProbe = LightProbe::create(gpDevice->getRenderContext(), actualPath, true, ResourceFormat::RGBA16Float, diffuseSamples, specSamples); pLightProbe->setPosW(position); pLightProbe->setIntensity(intensity); - mScene.addLightProbe(pLightProbe); + mBuilder.setLightProbe(pLightProbe); } return true; } - bool SceneImporter::createPathFrames(ObjectPath* pPath, const rapidjson::Value& jsonFramesArray) - { - // an array of key frames - if (jsonFramesArray.IsArray() == false) - { - return error("Camera path frames should be an array of key-frame objects"); - } - - for (uint32_t i = 0; i < jsonFramesArray.Size(); i++) - { - float time = 0; - glm::vec3 pos, target, up; - for (auto it = jsonFramesArray[i].MemberBegin(); it < jsonFramesArray[i].MemberEnd(); it++) - { - std::string key(it->name.GetString()); - auto& value = it->value; - bool b = true; - if (key == SceneKeys::kFrameTime) - { - if (value.IsNumber() == false) - { - error("Camera path time should be a number"); - b = false; - } - - time = (float)value.GetDouble(); - } - else if (key == SceneKeys::kCamPosition) - { - b = getFloatVec<3>(value, "Camera path position", &pos[0]); - } - else if (key == SceneKeys::kCamTarget) - { - b = getFloatVec<3>(value, "Camera path target", &target[0]); - } - else if (key == SceneKeys::kCamUp) - { - b = getFloatVec<3>(value, "Camera path up vector", &up[0]); - } - - if (b == false) - { - return false; - } - } - pPath->addKeyFrame(time, pos, target, up); - } - return true; - } - - ObjectPath::SharedPtr SceneImporter::createPath(const rapidjson::Value& jsonPath) - { - auto pPath = ObjectPath::create(); - - for (auto it = jsonPath.MemberBegin(); it != jsonPath.MemberEnd(); it++) - { - const std::string key(it->name.GetString()); - const auto& value = it->value; - - if (key == SceneKeys::kName) - { - if (value.IsString() == false) - { - error("Path name should be a string"); - return nullptr; - } - - std::string pathName(value.GetString()); - pPath->setName(pathName); - } - else if (key == SceneKeys::kPathLoop) - { - if (value.IsBool() == false) - { - error("Path loop should be a boolean value"); - return nullptr; - } - - bool b = value.GetBool(); - pPath->setAnimationRepeat(b); - } - else if (key == SceneKeys::kPathFrames) - { - if (createPathFrames(pPath.get(), value) == false) - { - return nullptr; - } - } - else if (key == SceneKeys::kAttachedObjects) - { - if (value.IsArray() == false) - { - error("Path object list should be an array"); - return nullptr; - } - - for (uint32_t i = 0; i < value.Size(); i++) - { - std::string type = value[i].FindMember(SceneKeys::kType)->value.GetString(); - std::string name = value[i].FindMember(SceneKeys::kName)->value.GetString(); - - pPath->attachObject(getMovableObject(type, name)); - } - } - else - { - error("Unknown token \"" + key + "\" when parsing camera path"); - return nullptr; - } - } - return pPath; - } - - bool SceneImporter::parsePaths(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parsePaths(const rapidjson::Value& jsonVal) { if (jsonVal.IsArray() == false) { return error("Paths should be an array"); } - for (uint32_t PathID = 0; PathID < jsonVal.Size(); PathID++) - { - auto pPath = createPath(jsonVal[PathID]); - if (pPath) - { - mScene.addPath(pPath); - } - else - { - return false; - } - } + logWarning("fscene paths are deprecated, please use Maya or other DCC tools to create a path directly in the model file"); return true; } - bool SceneImporter::parseActivePath(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseActivePath(const rapidjson::Value& jsonVal) { - if (mScene.getVersion() != 1) - { - return true; - } - - // Paths should already be initialized at this stage - if (jsonVal.IsString() == false) - { - return error("Active path should be a string."); - } - - std::string activePath = jsonVal.GetString(); - - // Find the path - for (uint32_t i = 0; i < mScene.getPathCount(); i++) - { - if (activePath == mScene.getPath(i)->getName()) - { - mScene.getPath(i)->attachObject(mScene.getActiveCamera()); - return true; - } - } - - return error("Active path \"" + activePath + "\" not found."); + logWarning("fscene paths are deprecated, please use Maya or other DCC tools to create a path directly in the model file"); + return true; } - bool SceneImporter::createCamera(const rapidjson::Value& jsonCamera) + bool SceneImporterImpl::parseCamera(const rapidjson::Value& jsonCamera) { auto pCamera = Camera::create(); std::string activePath; @@ -875,6 +799,16 @@ namespace Falcor } pCamera->setName(value.GetString()); } + else if (key == SceneKeys::kCamSpeed) + { + if (value.IsNumber() == false) + { + return error("Camera speed should be a number."); + } + + float f = (float)(value.GetDouble()); + mBuilder.setCameraSpeed(f); + } else if (key == SceneKeys::kCamPosition) { glm::vec3 pos; @@ -902,29 +836,8 @@ namespace Falcor } pCamera->setUpVector(up); } - else if (key == SceneKeys::kCamFovY) // Version 1 - { - if (mScene.getVersion() > 1) - { - return error("Camera FOV is only valid in scene version 1. Ignoring value."); - } - - if (value.IsNumber() == false) - { - return error("Camera's FOV should be a number"); - } - - // Convert to radians - float fovY = glm::radians((float)value.GetDouble()); - pCamera->setFocalLength(fovYToFocalLength(fovY, Camera::kDefaultFrameHeight)); - } else if (key == SceneKeys::kCamFocalLength) // Version 2 { - if (mScene.getVersion() != 2) - { - return error("Camera focal length is only valid in scene version 2. Ignoring value."); - } - if (value.IsNumber() == false) { return error("Camera's focal length should be a number"); @@ -949,58 +862,49 @@ namespace Falcor } pCamera->setAspectRatio((float)value.GetDouble()); } + else if (key == SceneKeys::kCamFocalDistance) + { + if (value.IsNumber() == false) + { + return error("Camera's focal distance should be a number"); + } + pCamera->setFocalDistance((float)value.GetDouble()); + } + else if (key == SceneKeys::kCamApertureRadius) + { + if (value.IsNumber() == false) + { + return error("Camera's aperture radius should be a number"); + } + pCamera->setApertureRadius((float)value.GetDouble()); + } else { return error("Invalid key found in cameras array. Key == " + key + "."); } } - if (isNameDuplicate(pCamera->getName(), mCameraMap, "cameras")) - { - return false; - } - else - { - mCameraMap[pCamera->getName()] = pCamera; - mScene.addCamera(pCamera); - } - + mBuilder.setCamera(pCamera); return true; } - bool SceneImporter::parseCameras(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseCameras(const rapidjson::Value& jsonVal) { if (jsonVal.IsArray() == false) { - return error("cameras section should be an array of objects."); + return error("Cameras section should be an array. If you want to use a single camera you can rename the section to `camera`"); } - - // Go over all the objects - for (uint32_t i = 0; i < jsonVal.Size(); i++) + if(jsonVal.Size() > 1) { - if (createCamera(jsonVal[i]) == false) - { - return false; - } + logWarning("The scene file contains multiple cameras. The first camera in the array will be used"); } - - // Set the active camera to camera 0. - mScene.setActiveCamera(0); - - return true; + return parseCamera(jsonVal[0]); } - bool SceneImporter::load(const std::string& filename, Model::LoadFlags modelLoadFlags, Scene::LoadFlags sceneLoadFlags) + bool SceneImporterImpl::load(const std::string& filename) { std::string fullpath; mFilename = filename; - mModelLoadFlags = modelLoadFlags; - mSceneLoadFlags = sceneLoadFlags; - - if (is_set(mSceneLoadFlags, Scene::LoadFlags::GenerateAreaLights)) - { - mModelLoadFlags |= Model::LoadFlags::BuffersAsShaderResource; - } if (findFileInDataDirectories(filename, fullpath)) { @@ -1027,11 +931,6 @@ namespace Falcor return false; } - if (is_set(mSceneLoadFlags, Scene::LoadFlags::GenerateAreaLights)) - { - mScene.createAreaLights(); - } - return true; } else @@ -1040,25 +939,24 @@ namespace Falcor } } - bool SceneImporter::parseAmbientIntensity(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseAmbientIntensity(const rapidjson::Value& jsonVal) { - logWarning("SceneImporter: Global ambient term is no longer supported. Ignoring value."); + logWarning("SceneImporterImpl: Global ambient term is no longer supported. Ignoring value."); return true; } - bool SceneImporter::parseLightingScale(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseLightingScale(const rapidjson::Value& jsonVal) { if (jsonVal.IsNumber() == false) { return error("Lighting scale should be a number."); } - float f = (float)(jsonVal.GetDouble()); - mScene.setLightingScale(f); + logWarning("Lighting scale is no longer supported"); return true; } - bool SceneImporter::parseCameraSpeed(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseCameraSpeed(const rapidjson::Value& jsonVal) { if (jsonVal.IsNumber() == false) { @@ -1066,62 +964,35 @@ namespace Falcor } float f = (float)(jsonVal.GetDouble()); - mScene.setCameraSpeed(f); + mBuilder.setCameraSpeed(f); return true; } - bool SceneImporter::parseActiveCamera(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseActiveCamera(const rapidjson::Value& jsonVal) { - // Cameras should already be initialized at this stage - if (jsonVal.IsString() == false) - { - return error("Active camera should be a string."); - } - - std::string activeCamera = jsonVal.GetString(); - - // Find the camera - for (uint32_t i = 0; i < mScene.getCameraCount(); i++) - { - if (activeCamera == mScene.getCamera(i)->getName()) - { - mScene.setActiveCamera(i); - return true; - } - } - - return error("Active camera \"" + activeCamera + "\" not found."); + logWarning(SceneKeys::kActiveCamera + std::string(" is not supported anymore. Using the first camera found")); + return true; } - bool SceneImporter::parseVersion(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseVersion(const rapidjson::Value& jsonVal) { - if (jsonVal.IsUint() == false) - { - return error("value should be an unsigned integer number"); - } - mScene.setVersion(jsonVal.GetUint()); + // Ignore this return true; } - bool SceneImporter::parseSceneUnit(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseSceneUnit(const rapidjson::Value& jsonVal) { if (jsonVal.IsNumber() == false) { return error("Scene unit should be a number."); } - float f = (float)(jsonVal.GetDouble()); - mScene.setSceneUnit(f); + logWarning("Scene unit is no longer supported"); return true; } - bool SceneImporter::parseEnvMap(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseEnvMap(const rapidjson::Value& jsonVal) { - if (mScene.getEnvironmentMap()) - { - return error("Scene can't have more then one environment map"); - } - if (jsonVal.IsString() == false) { return error(std::string(SceneKeys::kEnvMap) + " should be a string"); @@ -1136,109 +1007,23 @@ namespace Falcor } } - auto pTex = createTextureFromFile(filename, false, true); - mScene.setEnvironmentMap(pTex); + auto pTex = Texture::createFromFile(filename, false, true); + mBuilder.setEnvironmentMap(pTex); return true; } - bool SceneImporter::parseUserDefinedSection(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseUserDefinedSection(const rapidjson::Value& jsonVal) { if (jsonVal.IsObject() == false) { return error("User defined section should be a JSON object."); } - for (auto it = jsonVal.MemberBegin(); it != jsonVal.MemberEnd(); it++) - { - bool b; - Scene::UserVariable userVar; - std::string name(it->name.GetString()); - const auto& value = it->value; - // Check if this is a vector - if (value.IsArray()) - { - for (uint32_t i = 0; i < value.Size(); i++) - { - if (value[i].IsNumber() == false) - { - return error("User defined section contains an array, but some of the elements are not numbers."); - } - } - - switch (value.Size()) - { - case 2: - userVar.type = Scene::UserVariable::Type::Vec2; - b = getFloatVec<2>(value, "custom-field vec2", &userVar.vec2[0]); - break; - case 3: - userVar.type = Scene::UserVariable::Type::Vec3; - b = getFloatVec<3>(value, "custom-field vec3", &userVar.vec3[0]); - break; - case 4: - userVar.type = Scene::UserVariable::Type::Vec4; - b = getFloatVec<4>(value, "custom-field vec4", &userVar.vec4[0]); - break; - default: - userVar.type = Scene::UserVariable::Type::Vector; - b = getFloatVecAnySize(value, "vector of floats", userVar.vector); - break; - } - if (b == false) - { - return false; - } - } - else - { - // Not an array. Must be a literal - // The way rapidjson works, a uint is also an int, and a 32-bit number is also a 64-bit number, so the order in which we check the Type matters - if (value.IsUint()) - { - userVar.type = Scene::UserVariable::Type::Uint; - userVar.u32 = value.GetUint(); - } - else if (value.IsInt()) - { - userVar.type = Scene::UserVariable::Type::Int; - userVar.i32 = value.GetInt(); - } - else if (value.IsUint64()) - { - userVar.type = Scene::UserVariable::Type::Uint64; - userVar.u64 = value.GetUint64(); - } - else if (value.IsInt64()) - { - userVar.type = Scene::UserVariable::Type::Int64; - userVar.i64 = value.GetInt64(); - } - else if (value.IsDouble()) - { - userVar.type = Scene::UserVariable::Type::Double; - userVar.d64 = value.GetDouble(); - } - else if (value.IsString()) - { - userVar.type = Scene::UserVariable::Type::String; - userVar.str = value.GetString(); - } - else if (value.IsBool()) - { - userVar.type = Scene::UserVariable::Type::Bool; - userVar.b = value.GetBool(); - } - else - { - return error("Error when parsing custom-field \"" + name + "\". Field Type invalid. Must be a literal number, string boolean or an array of 2/3/4 numbers."); - } - } - mScene.addUserVariable(name, userVar); - } + logWarning("User defined section is no longer supported"); return true; } - bool SceneImporter::loadIncludeFile(const std::string& include) + bool SceneImporterImpl::loadIncludeFile(const std::string& include) { // Find the file std::string fullpath = mDirectory + '/' + include; @@ -1250,19 +1035,10 @@ namespace Falcor return error("Can't find include file " + include); } } - - Scene::SharedPtr pScene = Scene::create(); - SceneImporter::loadScene(*pScene, fullpath, mModelLoadFlags, mSceneLoadFlags); - if (pScene == nullptr) - { - return false; - } - mScene.merge(pScene.get()); - - return true; + return load(fullpath); } - bool SceneImporter::parseIncludes(const rapidjson::Value& jsonVal) + bool SceneImporterImpl::parseIncludes(const rapidjson::Value& jsonVal) { if (jsonVal.IsArray() == false) { @@ -1285,61 +1061,30 @@ namespace Falcor return true; } - bool SceneImporter::isNameDuplicate(const std::string& name, const ObjectMap& objectMap, const std::string& objectType) const - { - if (objectMap.find(name) != objectMap.end()) - { - const std::string msg = "Multiple " + objectType + " found the same name: " + name + ".\nObjects may not attach to paths correctly.\n\nContinue anyway?"; - - // If user pressed ok, return false to ignore duplicate - return msgBox(msg, MsgBoxType::OkCancel) == MsgBoxButton::Ok ? false : true; - } - - return false; - } - - IMovableObject::SharedPtr SceneImporter::getMovableObject(const std::string& type, const std::string& name) const - { - if (type == SceneKeys::kModelInstance) - { - return mInstanceMap.find(name)->second; - } - else if (type == SceneKeys::kCamera) - { - return mCameraMap.find(name)->second; - } - else if (type == SceneKeys::kLight) - { - return mLightMap.find(name)->second; - } - - should_not_get_here(); - return nullptr; - } - - const SceneImporter::FuncValue SceneImporter::kFunctionTable[] = + const SceneImporterImpl::FuncValue SceneImporterImpl::kFunctionTable[] = { // The order matters here. - {SceneKeys::kVersion, &SceneImporter::parseVersion}, - {SceneKeys::kSceneUnit, &SceneImporter::parseSceneUnit}, - {SceneKeys::kEnvMap, &SceneImporter::parseEnvMap}, - {SceneKeys::kAmbientIntensity, &SceneImporter::parseAmbientIntensity}, - {SceneKeys::kLightingScale, &SceneImporter::parseLightingScale}, - {SceneKeys::kCameraSpeed, &SceneImporter::parseCameraSpeed}, - - {SceneKeys::kModels, &SceneImporter::parseModels}, - {SceneKeys::kLights, &SceneImporter::parseLights}, - {SceneKeys::kLightProbes, &SceneImporter::parseLightProbes}, - {SceneKeys::kCameras, &SceneImporter::parseCameras}, - {SceneKeys::kActiveCamera, &SceneImporter::parseActiveCamera}, // Should come after ParseCameras - {SceneKeys::kUserDefined, &SceneImporter::parseUserDefinedSection}, - - {SceneKeys::kPaths, &SceneImporter::parsePaths}, - {SceneKeys::kActivePath, &SceneImporter::parseActivePath}, // Should come after ParsePaths - {SceneKeys::kInclude, &SceneImporter::parseIncludes} + {SceneKeys::kVersion, &SceneImporterImpl::parseVersion}, + {SceneKeys::kSceneUnit, &SceneImporterImpl::parseSceneUnit}, + {SceneKeys::kEnvMap, &SceneImporterImpl::parseEnvMap}, + {SceneKeys::kAmbientIntensity, &SceneImporterImpl::parseAmbientIntensity}, + {SceneKeys::kLightingScale, &SceneImporterImpl::parseLightingScale}, + {SceneKeys::kCameraSpeed, &SceneImporterImpl::parseCameraSpeed}, + + {SceneKeys::kModels, &SceneImporterImpl::parseModels}, + {SceneKeys::kLights, &SceneImporterImpl::parseLights}, + {SceneKeys::kLightProbes, &SceneImporterImpl::parseLightProbes}, + {SceneKeys::kCameras, &SceneImporterImpl::parseCameras}, + {SceneKeys::kCamera, &SceneImporterImpl::parseCamera}, + {SceneKeys::kActiveCamera, &SceneImporterImpl::parseActiveCamera}, // Should come after ParseCameras + {SceneKeys::kUserDefined, &SceneImporterImpl::parseUserDefinedSection}, + + {SceneKeys::kPaths, &SceneImporterImpl::parsePaths}, + {SceneKeys::kActivePath, &SceneImporterImpl::parseActivePath}, // Should come after ParsePaths + {SceneKeys::kInclude, &SceneImporterImpl::parseIncludes} }; - bool SceneImporter::validateSceneFile() + bool SceneImporterImpl::validateSceneFile() { // Make sure the top-level is valid for (auto it = mJDoc.MemberBegin(); it != mJDoc.MemberEnd(); it++) @@ -1365,7 +1110,7 @@ namespace Falcor return true; } - bool SceneImporter::topLevelLoop() + bool SceneImporterImpl::topLevelLoop() { if (validateSceneFile() == false) { @@ -1387,4 +1132,11 @@ namespace Falcor return true; } -} \ No newline at end of file + + + bool SceneImporter::import(const std::string& filename, SceneBuilder& builder) + { + SceneImporterImpl importer(builder); + return importer.load(filename); + } +} diff --git a/Framework/Source/Utils/Scripting/ScriptBindings.h b/Source/Falcor/Scene/Importers/SceneImporter.h similarity index 88% rename from Framework/Source/Utils/Scripting/ScriptBindings.h rename to Source/Falcor/Scene/Importers/SceneImporter.h index 380db8e90..5954c693d 100644 --- a/Framework/Source/Utils/Scripting/ScriptBindings.h +++ b/Source/Falcor/Scene/Importers/SceneImporter.h @@ -26,20 +26,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - -namespace pybind11 -{ - class module; -}; +#include "../SceneBuilder.h" namespace Falcor { - class ScriptBindings + class SceneImporter { public: - static void registerScriptingObjects(pybind11::module& m); - - static const char* kLoadScene; - static const char* kLoadRtScene; + static bool import(const std::string& filename, SceneBuilder& builder); + private: + SceneImporter() = default; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Light.cpp b/Source/Falcor/Scene/Lights/Light.cpp similarity index 52% rename from Framework/Source/Graphics/Light.cpp rename to Source/Falcor/Scene/Lights/Light.cpp index bb814023e..fa7894cbd 100644 --- a/Framework/Source/Graphics/Light.cpp +++ b/Source/Falcor/Scene/Lights/Light.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Light.h" -#include "Utils/Gui.h" -#include "API/Device.h" -#include "API/ConstantBuffer.h" -#include "API/Buffer.h" -#define _USE_MATH_DEFINES -#include -#include "Data/VertexAttrib.h" -#include "Graphics/Model/Model.h" -#include "Graphics/TextureHelper.h" -#include "API/Device.h" +#include "Utils/UI/Gui.h" namespace Falcor { @@ -50,8 +41,33 @@ namespace Falcor return true; } - void Light::setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, const std::string& varName) + void Light::setIntensity(const glm::vec3& intensity) { + mData.intensity = intensity; + } + + Light::Changes Light::beginFrame() + { + mChanges = Changes::None; + if (mPrevData.posW != mData.posW) mChanges |= Changes::Position; + if (mPrevData.dirW != mData.dirW) mChanges |= Changes::Direction; + if (mPrevData.intensity != mData.intensity) mChanges |= Changes::Intensity; + if (mPrevData.openingAngle != mData.openingAngle) mChanges |= Changes::SurfaceArea; + if (mPrevData.penumbraAngle != mData.penumbraAngle) mChanges |= Changes::SurfaceArea; + if (mPrevData.surfaceArea != mData.surfaceArea) mChanges |= Changes::SurfaceArea; + + assert(mPrevData.tangent == mData.tangent); + assert(mPrevData.bitangent == mData.bitangent); + assert(mPrevData.transMat == mData.transMat); + + mPrevData = mData; + + return getChanges(); + } + + void Light::setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) + { + ConstantBuffer* pCb = pBlock->getDefaultConstantBuffer().get(); size_t offset = pCb->getVariableOffset(varName); #if _LOG_ENABLED @@ -62,16 +78,16 @@ namespace Falcor #undef check_offset #endif - setIntoProgramVars(pVars, pCb, offset); + setIntoVariableBuffer(pCb, offset); } - void Light::setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, size_t offset) + void Light::setIntoVariableBuffer(VariablesBuffer* pBuffer, size_t offset) { static_assert(kDataSize % sizeof(vec4) == 0, "LightData size should be a multiple of 16"); - assert(offset + kDataSize <= pCb->getSize()); + assert(offset + kDataSize <= pBuffer->getSize()); // Set everything except for the material - pCb->setBlob(&mData, offset, kDataSize); + pBuffer->setBlob(&mData, offset, kDataSize); } glm::vec3 Light::getColorForUI() @@ -114,7 +130,7 @@ namespace Falcor void Light::setColorFromUI(const glm::vec3& uiColor) { mUiLightIntensityColor = uiColor; - mData.intensity = (mUiLightIntensityColor * mUiLightIntensityScale); + setIntensity(mUiLightIntensityColor * mUiLightIntensityScale); updateAreaLightIntensity(mData); } @@ -141,29 +157,28 @@ namespace Falcor void Light::setIntensityFromUI(float intensity) { mUiLightIntensityScale = intensity; - mData.intensity = (mUiLightIntensityColor * mUiLightIntensityScale); + setIntensity(mUiLightIntensityColor * mUiLightIntensityScale); updateAreaLightIntensity(mData); } void Light::renderUI(Gui* pGui, const char* group) { - if (!group || pGui->beginGroup(group)) + if (!group) group = "General Light Settings"; + Gui::Group g(pGui, group); + if (g.open()) { glm::vec3 color = getColorForUI(); - if (pGui->addRgbColor("Color", color)) + if (g.rgbColor("Color", color)) { setColorFromUI(color); } float intensity = getIntensityForUI(); - if (pGui->addFloatVar("Intensity", intensity)) + if (g.var("Intensity", intensity)) { setIntensityFromUI(intensity); } - if (group) - { - pGui->endGroup(); - } + g.release(); } } @@ -182,17 +197,16 @@ namespace Falcor void DirectionalLight::renderUI(Gui* pGui, const char* group) { - if (!group || pGui->beginGroup(group)) + if (!group) group = "Directional Light Settings"; + Gui::Group g(pGui, group); + if (g.open()) { - if (pGui->addDirectionWidget("Direction", mData.dirW)) + if (g.direction("Direction", mData.dirW)) { setWorldDirection(mData.dirW); } Light::renderUI(pGui); - if (group) - { - pGui->endGroup(); - } + g.release(); } } @@ -215,11 +229,6 @@ namespace Falcor return luminance(mData.intensity) * surfaceArea; } - void DirectionalLight::move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) - { - logError("DirectionalLight::move() is not used and thus not implemented for now."); - } - PointLight::SharedPtr PointLight::create() { PointLight* pLight = new PointLight; @@ -233,6 +242,16 @@ namespace Falcor PointLight::~PointLight() = default; + void PointLight::setWorldDirection(const glm::vec3& dir) + { + mData.dirW = normalize(dir); + } + + void PointLight::setWorldPosition(const glm::vec3& pos) + { + mData.posW = pos; + } + float PointLight::getPower() const { return luminance(mData.intensity) * 4.f * (float)M_PI; @@ -240,40 +259,42 @@ namespace Falcor void PointLight::renderUI(Gui* pGui, const char* group) { - if (!group || pGui->beginGroup(group)) + if (!group) group = "Point Light Settings"; + Gui::Group g(pGui, group); + if (g.open()) { - pGui->addFloat3Var("World Position", mData.posW, -FLT_MAX, FLT_MAX); - pGui->addDirectionWidget("Direction", mData.dirW); + g.var("World Position", mData.posW, -FLT_MAX, FLT_MAX); + g.direction("Direction", mData.dirW); - if (pGui->addFloatVar("Opening Angle", mData.openingAngle, 0.f, (float)M_PI)) + if (g.var("Opening Angle", mData.openingAngle, 0.f, (float)M_PI)) { setOpeningAngle(mData.openingAngle); } - if (pGui->addFloatVar("Penumbra Width", mData.penumbraAngle, 0.f, (float)M_PI)) + if (g.var("Penumbra Width", mData.penumbraAngle, 0.f, (float)M_PI)) { setPenumbraAngle(mData.penumbraAngle); } Light::renderUI(pGui); - if (group) - { - pGui->endGroup(); - } + g.release(); } } void PointLight::setOpeningAngle(float openingAngle) { openingAngle = glm::clamp(openingAngle, 0.f, (float)M_PI); + if (openingAngle == mData.openingAngle) return; + mData.openingAngle = openingAngle; /* Prepare an auxiliary cosine of the opening angle to quickly check whether we're within the cone of a spot light */ mData.cosOpeningAngle = cos(openingAngle); } - void PointLight::move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) + void PointLight::setPenumbraAngle(float angle) { - mData.posW = position; - mData.dirW = target - position; + angle = glm::clamp(angle, 0.0f, mData.openingAngle); + if (mData.penumbraAngle == angle) return; + mData.penumbraAngle = angle; } AreaLight::SharedPtr AreaLight::create() @@ -293,16 +314,17 @@ namespace Falcor return luminance(mAreaLightData.intensity) * (float)M_PI * mAreaLightData.surfaceArea; } - void AreaLight::setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, const std::string& varName) + void AreaLight::setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) { // Set data except for material and mesh buffers - size_t offset = pCb->getVariableOffset(varName); + VariablesBuffer* pBuffer = pBlock->getDefaultConstantBuffer().get(); + size_t offset = pBuffer->getVariableOffset(varName); static_assert(kDataSize % sizeof(vec4) == 0, "AreaLightData size should be a multiple of 16"); - assert(offset + kAreaLightDataSize <= pCb->getSize()); - pCb->setBlob(&mData, offset, kAreaLightDataSize); + assert(offset + kAreaLightDataSize <= pBuffer->getSize()); + pBuffer->setBlob(&mData, offset, kAreaLightDataSize); #if _LOG_ENABLED -#define check_offset(_a) {static bool b = true; if(b) {assert(checkOffset("AreaLightData", pCb->getVariableOffset(varName + "." + #_a) - offset, offsetof(AreaLightData, _a), #_a));} b = false;} +#define check_offset(_a) {static bool b = true; if(b) {assert(checkOffset("AreaLightData", pBuffer->getVariableOffset(varName + "." + #_a) - offset, offsetof(AreaLightData, _a), #_a));} b = false;} check_offset(dirW); check_offset(intensity); check_offset(tangent); @@ -313,233 +335,205 @@ namespace Falcor #endif // Set buffers and material - const ParameterBlock::SharedPtr& pBlock = pVars->getDefaultBlock(); pBlock->setRawBuffer(varName + ".resources.indexBuffer", mpIndexBuffer); pBlock->setRawBuffer(varName + ".resources.vertexBuffer", mpVertexBuffer); - pBlock->setRawBuffer(varName + ".resources.texCoordBuffer", mpTexCoordBuffer); pBlock->setRawBuffer(varName + ".resources.meshCDFBuffer", mpMeshCDFBuffer); - - std::string matVarName = varName + ".resources.material"; - mpMeshInstance->getObject()->getMaterial()->setIntoProgramVars(pVars, pCb, matVarName.c_str()); } - void AreaLight::setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, size_t offset) + void AreaLight::setIntoVariableBuffer(VariablesBuffer* pBuffer, size_t offset) { - logWarning("AreaLight::setIntoProgramVars() - Area light data contains resources that cannot be bound by offset. Ignoring call."); + logWarning("AreaLight::setIntoVariableBuffer() - Area light data contains resources that cannot be bound by offset. Ignoring call."); } void AreaLight::renderUI(Gui* pGui, const char* group) { - if (!group || pGui->beginGroup(group)) + if (!group) group = "Area Light Settings"; + Gui::Group g(pGui, group); + if (g.open()) { - if (mpMeshInstance) - { + //if (mpMeshInstance) + //{ // TODO: Premultiply by mpModelInstance->getTransformMatrix() or do it in the shader - vec3 posW = mpMeshInstance->getTransformMatrix()[3]; - if (pGui->addFloat3Var("World Position", posW, -FLT_MAX, FLT_MAX)) - { - mpMeshInstance->setTranslation(posW, true); - } - } - - float intensity = mAreaLightData.intensity.r; - if (pGui->addFloatVar("Intensity", intensity, 0.0f)) - { - mAreaLightData.intensity = vec3(intensity); - } - - if (group) - { - pGui->endGroup(); - } + // vec3 posW = mpMeshInstance->getTransformMatrix()[3]; + // if (pGui->addFloat3Var("World Position", posW, -FLT_MAX, FLT_MAX)) + // { + // mpMeshInstance->setTranslation(posW, true); + // } + //} + +// float intensity = mAreaLightData.intensity.r; +// if (pGui->addFloatVar("Intensity", intensity, 0.0f)) +// { +// mAreaLightData.intensity = vec3(intensity); +// } + g.release(); } } - void AreaLight::setMeshData(const Model::MeshInstance::SharedPtr& pMeshInstance) + void AreaLight::setMeshData(const std::shared_ptr& pScene, uint32_t instanceId) { - if (pMeshInstance && pMeshInstance != mpMeshInstance) + if (pScene && instanceId < pScene->getMeshInstanceCount()) { - const auto& pMesh = pMeshInstance->getObject(); - assert(pMesh != nullptr); + const MeshInstanceData& instance = pScene->getMeshInstance(instanceId); - mpMeshInstance = pMeshInstance; + mMeshDesc = pScene->getMesh(instance.meshID); + mAreaLightData.materialId = mMeshDesc.materialID; + mInstanceId = instanceId; + mpScene = pScene; // Fetch the mesh instance transformation // TODO: Premultiply by mpModelInstance->getTransformMatrix() or do it in the shader - mAreaLightData.transMat = mpMeshInstance->getTransformMatrix(); + // #SCENE TODO: Interface for getting instance transform? + // mAreaLightData.transMat = pScene->mpAnimationController()->getGlobalMatrices()[instance.globalMatrixID]; - const auto& vao = pMesh->getVao(); - setIndexBuffer(vao->getIndexBuffer()); + const auto& pVao = pScene->getVao(); + mpIndexBuffer = pVao->getIndexBuffer(); mAreaLightData.numTriangles = uint32_t(mpIndexBuffer->getSize() / sizeof(glm::ivec3)); - int32_t posIdx = vao->getElementIndexByLocation(VERTEX_POSITION_LOC).vbIndex; - assert(posIdx != Vao::ElementDesc::kInvalidIndex); - setPositionsBuffer(vao->getVertexBuffer(posIdx)); - - const int32_t uvIdx = vao->getElementIndexByLocation(VERTEX_TEXCOORD_LOC).vbIndex; - bool hasUv = uvIdx != Vao::ElementDesc::kInvalidIndex; - if (hasUv) - { - setTexCoordBuffer(vao->getVertexBuffer(VERTEX_TEXCOORD_LOC)); - } + // TODO SCENE: Remove hard-code index once SceneBuilder buffer/drawlist indices aren't confined to the cpp + mpVertexBuffer = pVao->getVertexBuffer(0); // Static mesh data in index 0, SceneBuilder::sStaticMeshIndex + assert(mpVertexBuffer != nullptr); // Compute surface area of the mesh and generate probability // densities for importance sampling a triangle mesh - computeSurfaceArea(); + computeSurfaceArea(pScene); - // Check if this mesh has a material - const Material::SharedPtr& pMaterial = pMesh->getMaterial(); - if (pMaterial) - { - mAreaLightData.intensity = pMaterial->getEmissiveColor(); - } + const Material::SharedPtr& pMaterial = pScene->getMaterial(mMeshDesc.materialID); + assert(pMaterial != nullptr); + mAreaLightData.intensity = pMaterial->getEmissiveColor(); } } - void AreaLight::computeSurfaceArea() + void AreaLight::computeSurfaceArea(const std::shared_ptr& pScene) { - if (mpMeshInstance && mpVertexBuffer && mpIndexBuffer) + assert(pScene != nullptr && mInstanceId < pScene->getMeshInstanceCount()); + assert(pScene->getVao()->getPrimitiveTopology() == Vao::Topology::TriangleList); + + const uint32_t primitiveCount = mMeshDesc.indexCount / 3; + if (primitiveCount != 2 || mMeshDesc.vertexCount != 4) { - const auto& pMesh = mpMeshInstance->getObject(); - assert(pMesh != nullptr); + logWarning("Only support sampling of rectangular light sources made of 2 triangles."); + return; + } - if (mpMeshInstance->getObject()->getPrimitiveCount() != 2 || mpMeshInstance->getObject()->getVertexCount() != 4) - { - logWarning("Only support sampling of rectangular light sources made of 2 triangles."); - return; - } + // Read data from the buffers + const glm::ivec3* pIndices = (const glm::ivec3*)mpIndexBuffer->map(Buffer::MapType::Read); + const uint8_t* pVb = (const uint8_t*)mpVertexBuffer->map(Buffer::MapType::Read); + // TODO SCENE: Remove hard-code index once SceneBuilder buffer/drawlist indices aren't confined to the cpp + const uint32_t vertexStride = pScene->getVao()->getVertexLayout()->getBufferLayout(0)->getStride(); - // Read data from the buffers - const glm::ivec3* pIndices = (const glm::ivec3*)mpIndexBuffer->map(Buffer::MapType::Read); - const glm::vec3* pVertices = (const glm::vec3*)mpVertexBuffer->map(Buffer::MapType::Read); + // The buffer has an array-of-structs layout, so some extra work is necessary + auto getPos = [&](uint32_t i) { return *(vec3*)(pVb + vertexStride * i); }; - // Calculate surface area of the mesh - mAreaLightData.surfaceArea = 0.f; - mMeshCDF.push_back(0.f); - for (uint32_t i = 0; i < pMesh->getPrimitiveCount(); ++i) - { - glm::ivec3 pId = pIndices[i]; - const vec3 p0(pVertices[pId.x]), p1(pVertices[pId.y]), p2(pVertices[pId.z]); + // Calculate surface area of the mesh + mAreaLightData.surfaceArea = 0.f; + mMeshCDF.push_back(0.f); + for (uint32_t i = 0; i < primitiveCount; ++i) + { + glm::ivec3 pId = pIndices[i]; + const vec3 p0(getPos(pId.x)), p1(getPos(pId.y)), p2(getPos(pId.z)); - mAreaLightData.surfaceArea += 0.5f * glm::length(glm::cross(p1 - p0, p2 - p0)); + mAreaLightData.surfaceArea += 0.5f * glm::length(glm::cross(p1 - p0, p2 - p0)); - // Add an entry using surface area measure as the discrete probability - mMeshCDF.push_back(mMeshCDF[mMeshCDF.size() - 1] + mAreaLightData.surfaceArea); - } + // Add an entry using surface area measure as the discrete probability + mMeshCDF.push_back(mMeshCDF[mMeshCDF.size() - 1] + mAreaLightData.surfaceArea); + } - // Normalize the probability densities - if (mAreaLightData.surfaceArea > 0.f) + // Normalize the probability densities + if (mAreaLightData.surfaceArea > 0.f) + { + float invSurfaceArea = 1.f / mAreaLightData.surfaceArea; + for (uint32_t i = 1; i < mMeshCDF.size(); ++i) { - float invSurfaceArea = 1.f / mAreaLightData.surfaceArea; - for (uint32_t i = 1; i < mMeshCDF.size(); ++i) - { - mMeshCDF[i] *= invSurfaceArea; - } - - mMeshCDF[mMeshCDF.size() - 1] = 1.f; + mMeshCDF[i] *= invSurfaceArea; } - // Calculate basis tangent vectors and their lengths - ivec3 pId = pIndices[0]; - const vec3 p0(pVertices[pId.x]), p1(pVertices[pId.y]), p2(pVertices[pId.z]); + mMeshCDF[mMeshCDF.size() - 1] = 1.f; + } - mAreaLightData.tangent = p0 - p1; - mAreaLightData.bitangent = p2 - p1; + // Calculate basis tangent vectors and their lengths + ivec3 pId = pIndices[0]; + const vec3 p0(getPos(pId.x)), p1(getPos(pId.y)), p2(getPos(pId.z)); - // Create a CDF buffer - mpMeshCDFBuffer.reset(); - mpMeshCDFBuffer = Buffer::create(sizeof(mMeshCDF[0])*mMeshCDF.size(), Buffer::BindFlags::ShaderResource, Buffer::CpuAccess::None, mMeshCDF.data()); + mAreaLightData.tangent = p0 - p1; + mAreaLightData.bitangent = p2 - p1; - // Set the world position and world direction of this light - if (mpIndexBuffer->getSize() != 0 && mpVertexBuffer->getSize() != 0) - { - glm::vec3 boxMin = pVertices[0]; - glm::vec3 boxMax = pVertices[0]; - for (uint32_t id = 1; id < mpMeshInstance->getObject()->getVertexCount(); ++id) - { - boxMin = glm::min(boxMin, pVertices[id]); - boxMax = glm::max(boxMax, pVertices[id]); - } + // Create a CDF buffer + mpMeshCDFBuffer.reset(); + mpMeshCDFBuffer = Buffer::create(sizeof(mMeshCDF[0])*mMeshCDF.size(), Buffer::BindFlags::ShaderResource, Buffer::CpuAccess::None, mMeshCDF.data()); - mAreaLightData.posW = BoundingBox::fromMinMax(boxMin, boxMax).center; + // Set the world position and world direction of this light + if (mpIndexBuffer->getSize() != 0 && mpVertexBuffer->getSize() != 0) + { + glm::vec3 boxMin = getPos(0); + glm::vec3 boxMax = boxMin; + for (uint32_t id = 1; id < mMeshDesc.vertexCount; id++) + { + boxMin = glm::min(boxMin, getPos(id)); + boxMax = glm::max(boxMax, getPos(id)); + } - // This holds only for planar light sources - const glm::vec3& p0 = pVertices[pIndices[0].x]; - const glm::vec3& p1 = pVertices[pIndices[0].y]; - const glm::vec3& p2 = pVertices[pIndices[0].z]; + mAreaLightData.posW = BoundingBox::fromMinMax(boxMin, boxMax).center; - // Take the normal of the first triangle as a light normal - mAreaLightData.dirW = normalize(cross(p1 - p0, p2 - p0)); + // This holds only for planar light sources + const glm::vec3& p0 = getPos(pIndices[0].x); + const glm::vec3& p1 = getPos(pIndices[0].y); + const glm::vec3& p2 = getPos(pIndices[0].z); - // Save the axis-aligned bounding box - mAreaLightData.aabbMin = boxMin; - mAreaLightData.aabbMax = boxMax; - } + // Take the normal of the first triangle as a light normal + mAreaLightData.dirW = normalize(cross(p1 - p0, p2 - p0)); - mpIndexBuffer->unmap(); - mpVertexBuffer->unmap(); + // Save the axis-aligned bounding box + mAreaLightData.aabbMin = boxMin; + mAreaLightData.aabbMax = boxMax; } - } - void AreaLight::move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) - { - // Override target and up - vec3 stillTarget = position + vec3(0, 0, 1); - vec3 stillUp = vec3(0, 1, 0); - mpMeshInstance->move(position, stillTarget, stillUp); + mpIndexBuffer->unmap(); + mpVertexBuffer->unmap(); } - AreaLight::SharedPtr createAreaLight(const Model::MeshInstance::SharedPtr& pMeshInstance) + AreaLight::SharedPtr createAreaLight(const std::shared_ptr& pScene, uint32_t instanceId) { // Create an area light AreaLight::SharedPtr pAreaLight = AreaLight::create(); if (pAreaLight) { // Set the geometry mesh - pAreaLight->setMeshData(pMeshInstance); + pAreaLight->setMeshData(pScene, instanceId); } return pAreaLight; } - std::vector createAreaLightsForModel(const Model* pModel) + // #SCENE Not used right now + std::vector createAreaLightsForScene(const std::shared_ptr& pScene) { - assert(pModel); + assert(pScene); std::vector areaLights; // Get meshes for this model - for (uint32_t meshId = 0; meshId < pModel->getMeshCount(); ++meshId) + for (uint32_t instanceId = 0; instanceId < pScene->getMeshInstanceCount(); instanceId++) { - const Mesh::SharedPtr& pMesh = pModel->getMesh(meshId); - - // Obtain mesh instances for this mesh - for (uint32_t instanceId = 0; instanceId < pModel->getMeshInstanceCount(meshId); ++instanceId) + uint32_t meshId = pScene->getMeshInstance(instanceId).meshID; + uint32_t materialId = pScene->getMesh(meshId).materialID; + if (pScene->getMaterial(materialId)->isEmissive()) { - // Check if this mesh has an emissive material - const Material::SharedPtr& pMaterial = pMesh->getMaterial(); - if (pMaterial) - { - if (pMaterial->isEmissive()) - { - // TODO: Create one area light per model instance, pass it the model instance transform - areaLights.push_back(createAreaLight(pModel->getMeshInstance(meshId, instanceId))); - } - } + areaLights.push_back(createAreaLight(pScene, instanceId)); } } return areaLights; } // Code for analytic area lights. - AnalyticAreaLight::SharedPtr AnalyticAreaLight::create() + AnalyticAreaLight::SharedPtr AnalyticAreaLight::create(uint32_t type) { - AnalyticAreaLight* pLight = new AnalyticAreaLight; + AnalyticAreaLight* pLight = new AnalyticAreaLight(type); return SharedPtr(pLight); } - AnalyticAreaLight::AnalyticAreaLight() + AnalyticAreaLight::AnalyticAreaLight(uint32_t type) { - mData.type = LightAreaRect; + mData.type = type; mData.tangent = float3(1, 0, 0); mData.bitangent = float3(0, 1, 0); mData.surfaceArea = 4.0f; @@ -557,14 +551,13 @@ namespace Falcor void AnalyticAreaLight::renderUI(Gui* pGui, const char* group) { - if (!group || pGui->beginGroup(group)) + if (!group) group = "Analytic Area Light Settings"; + Gui::Group g(pGui, group); + if (g.open()) { Light::renderUI(pGui); - if (group) - { - pGui->endGroup(); - } + g.release(); } } @@ -609,9 +602,12 @@ namespace Falcor } } - void AnalyticAreaLight::move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) + SCRIPT_BINDING(Light) { - mTransformMatrix = glm::inverse(glm::lookAt(position, 2.0f*position - target, up)); // Some math gymnastics to compensate for lookat returning the inverse matrix (suitable for camera), while we want to point the light source - update(); + m.regClass(Light); + m.regClass(DirectionalLight); + m.regClass(PointLight); + m.regClass(AnalyticAreaLight); + m.regClass(AreaLight); } } diff --git a/Framework/Source/Graphics/Light.h b/Source/Falcor/Scene/Lights/Light.h similarity index 66% rename from Framework/Source/Graphics/Light.h rename to Source/Falcor/Scene/Lights/Light.h index d486e2ba3..35b419755 100644 --- a/Framework/Source/Graphics/Light.h +++ b/Source/Falcor/Scene/Lights/Light.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,46 +26,34 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include -#include "glm/geometric.hpp" -#include "API/Texture.h" -#include "glm/mat4x4.hpp" #include "Data/HostDeviceData.h" -#include "Utils/Gui.h" -#include "Graphics/Paths/MovableObject.h" -#include "Graphics/Model/Model.h" namespace Falcor { - class ConstantBuffer; - class Gui; + class Scene; /** Base class for light sources. All light sources should inherit from this. */ - class Light : public IMovableObject, public inherit_shared_from_this + class dlldecl Light { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - SharedPtr shared_from_this() { return inherit_shared_from_this::shared_from_this(); } + using ConstSharedPtrRef = const SharedPtr&; - Light() = default; virtual ~Light() = default; /** Set the light parameters into a program. To use this you need to include/import 'ShaderCommon' inside your shader. - \param[in] pVars The program vars to set the parameters into. - \param[in] pBuffer The constant buffer to set the parameters into. - \param[in] varName The name of the light variable in the program. + \param[in] pBlock The parameter block to set the parameters into. + \param[in] varName The name of the LightData variable in the parameter block. */ - virtual void setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, const std::string& varName); + virtual void setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName); /** Set the light parameters into a program. To use this you need to include/import 'ShaderCommon' inside your shader. - \param[in] pVars The program vars to set the parameters into. - \param[in] pBuffer The constant buffer to set the parameters into. + \param[in] pCb The constant buffer to set the parameters into. \param[in] offset Byte offset into the constant buffer to set data to. */ - virtual void setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, size_t offset); + virtual void setIntoVariableBuffer(VariablesBuffer* pBuffer, size_t offset); /** Render UI elements for this light. \param[in] pGui The GUI to create the elements with @@ -97,7 +85,29 @@ namespace Falcor */ static uint32_t getShaderStructSize() { return kDataSize; } + /** Set the light intensity. + */ + virtual void setIntensity(const glm::vec3& intensity); + + enum class Changes + { + None = 0x0, + Position = 0x1, + Direction = 0x2, + Intensity = 0x4, + SurfaceArea = 0x8, + }; + + /** Begin a new frame. Returns the changes from the previous frame + */ + Changes beginFrame(); + + /** Returns the changes from the previous frame + */ + Changes getChanges() const { return mChanges; } + protected: + Light() = default; static const size_t kDataSize = sizeof(LightData); @@ -112,20 +122,19 @@ namespace Falcor /* These two variables track mData values for consistent UI operation.*/ glm::vec3 mUiLightIntensityColor = glm::vec3(0.5f, 0.5f, 0.5f); float mUiLightIntensityScale = 1.0f; - LightData mData; + LightData mData, mPrevData; + Changes mChanges = Changes::None; }; /** Directional light source. */ - class DirectionalLight : public Light, public std::enable_shared_from_this + class dlldecl DirectionalLight : public Light { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; static SharedPtr create(); - - DirectionalLight(); ~DirectionalLight(); /** Render UI elements for this light. @@ -138,11 +147,6 @@ namespace Falcor */ void setWorldDirection(const glm::vec3& dir); - /** Set the light intensity. - \param[in] intensity Vec3 corresponding to RGB intensity - */ - void setIntensity(const glm::vec3& intensity) { mData.intensity = intensity; } - /** Set the scene parameters */ void setWorldParams(const glm::vec3& center, float radius); @@ -151,35 +155,25 @@ namespace Falcor */ const glm::vec3& getWorldDirection() const { return mData.dirW; } - /** Get the light intensity. - */ - const glm::vec3& getIntensity() const { return mData.intensity; } - /** Get total light power (needed for light picking) */ float getPower() const override; - /** IMovableObject interface - */ - void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override; - private: - + DirectionalLight(); float mDistance = 1e3f; ///< Scene bounding radius is required to move the light position sufficiently far away vec3 mCenter; }; /** Simple infinitely-small point light with quadratic attenuation */ - class PointLight : public Light, public std::enable_shared_from_this + class dlldecl PointLight : public Light { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; static SharedPtr create(); - - PointLight(); ~PointLight(); /** Render UI elements for this light. @@ -194,15 +188,11 @@ namespace Falcor /** Set the light's world-space position */ - void setWorldPosition(const glm::vec3& pos) { mData.posW = pos; } - - /** Set the light's world-space position - */ - void setWorldDirection(const glm::vec3& dir) { mData.dirW = dir; } + void setWorldPosition(const glm::vec3& pos); - /** Set the light intensity. + /** Set the light's world-space direction. */ - void setIntensity(const glm::vec3& intensity) { mData.intensity = intensity; } + void setWorldDirection(const glm::vec3& dir); /** Set the cone opening angle for use as a spot light \param[in] openingAngle Angle in radians. @@ -228,17 +218,14 @@ namespace Falcor /** Set the penumbra angle \param[in] angle Angle in radians */ - void setPenumbraAngle(float angle) { mData.penumbraAngle = glm::clamp(angle, 0.0f, mData.openingAngle);; } + void setPenumbraAngle(float angle); /** Get the opening angle */ float getOpeningAngle() const { return mData.openingAngle; } - /** IMovableObject interface - */ - void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override; - private: + PointLight(); }; /** @@ -247,7 +234,7 @@ namespace Falcor This class is used to simulate area light sources. All emissive materials are treated as area light sources. */ - class AreaLight : public Light, public std::enable_shared_from_this + class dlldecl AreaLight : public Light { public: using SharedPtr = std::shared_ptr; @@ -255,7 +242,6 @@ namespace Falcor static SharedPtr create(); - AreaLight(); ~AreaLight(); /** Get total light power (needed for light picking) @@ -264,16 +250,15 @@ namespace Falcor /** Set the light parameters into a program. To use this you need to include/import 'ShaderCommon' inside your shader and declare a constant buffer to bind the values to using the AREA_LIGHTS() macro defined in HostDeviceSharedMacros.h - \param[in] pVars The program vars to set the parameters into. - \param[in] pBuffer The constant buffer to set the parameters into. - \param[in] varName The name of the declared variable in the program. "gAreaLights" by default. + \param[in] pBlock The parameter block to set the parameters into. + \param[in] varName The name of the declared variable in the parameter block. */ - virtual void setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, const std::string& varName) override; + virtual void setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) override; /** Do not use this overload for area lights. Area light data contains resources that cannot be bound using an offset when there is an array of light data. Calling this function will do nothing except log a warning. */ - virtual void setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, size_t offset) override; + virtual void setIntoVariableBuffer(VariablesBuffer* pBuffer, size_t offset) override; /** Render UI elements for this light. \param[in] pGui The GUI to create the elements with @@ -282,20 +267,10 @@ namespace Falcor void renderUI(Gui* pGui, const char* group = nullptr) override; /** Set the geometry mesh for this light - \param[in] pModel Model that contains the geometry mesh for this light - \param[in] meshId Geometry mesh id within the model - \param[in] instanceId Geometry mesh instance id - */ - void setMeshData(const Model::MeshInstance::SharedPtr& pMeshInstance); - - /** Obtain the geometry mesh for this light - \return Mesh instance for this light - */ - const Model::MeshInstance::SharedPtr& getMeshData() const { return mpMeshInstance; } - - /** Compute surface area of the mesh + \param[in] pScene The scene the light is in + \param[in] instanceId Mesh instance id */ - void computeSurfaceArea(); + void setMeshData(const std::shared_ptr& pScene, uint32_t instanceId); /** Get surface area of the mesh */ @@ -304,34 +279,6 @@ namespace Falcor /** Get the probability distribution of the mesh */ const std::vector& getMeshCDF() const { return mMeshCDF; } - - /** Set the index buffer - \param[in] indexBuf Buffer containing mesh indices - */ - void setIndexBuffer(const Buffer::SharedPtr& indexBuf) { mpIndexBuffer = indexBuf; } - - /** Get the index buffer. - */ - const Buffer::SharedPtr& getIndexBuffer() const { return mpIndexBuffer; } - - /** Set the vertex buffer. - \param[in] vertexBuf Buffer containing mesh vertices - */ - void setPositionsBuffer(const Buffer::SharedPtr& vertexBuf) { mpVertexBuffer = vertexBuf; } - - /** Get the vertex buffer. - */ - const Buffer::SharedPtr& getPositionsBuffer() const { return mpVertexBuffer; } - - /** Set the texture coordinate/UV buffer. - \param[in] texCoordBuf Buffer containing texture coordinates - */ - void setTexCoordBuffer(const Buffer::SharedPtr& texCoordBuf) { mpTexCoordBuffer = texCoordBuf; } - - /** Get texture coordinate buffer. - */ - const Buffer::SharedPtr& getTexCoordBuffer() const { return mpTexCoordBuffer; } - /** Set the mesh CDF buffer. \param[in] meshCDF Buffer containing mesh CDF data */ @@ -341,58 +288,55 @@ namespace Falcor */ const Buffer::SharedPtr& getMeshCDFBuffer() const { return mpMeshCDFBuffer; } - /** IMovableObject interface - */ - void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override; - /** Gets the size of a single light data struct in bytes */ static uint32_t getShaderStructSize() { return kAreaLightDataSize; } private: + AreaLight(); + void computeSurfaceArea(const std::shared_ptr& pScene); static const size_t kAreaLightDataSize = sizeof(AreaLightData) - sizeof(AreaLightResources); AreaLightData mAreaLightData; - Model::MeshInstance::SharedPtr mpMeshInstance; ///< Geometry mesh data + std::weak_ptr mpScene; ///< Scene this area light is a part of + uint32_t mInstanceId; ///< Mesh Instance Id from the scene this light was created from + MeshDesc mMeshDesc; ///< Where the actual mesh is inside the buffers Buffer::SharedPtr mpIndexBuffer; ///< Buffer for indices Buffer::SharedPtr mpVertexBuffer; ///< Buffer for vertices - Buffer::SharedPtr mpTexCoordBuffer; ///< Buffer for texcoord Buffer::SharedPtr mpMeshCDFBuffer; ///< Buffer for mesh Cumulative distribution function (CDF) std::vector mMeshCDF; ///< CDF function for importance sampling a triangle mesh }; - AreaLight::SharedPtr createAreaLight(const Model::MeshInstance::SharedPtr& pMeshInstance); - std::vector createAreaLightsForModel(const Model* pModel); + dlldecl AreaLight::SharedPtr createAreaLight(const std::shared_ptr& pScene, uint32_t instanceId); + dlldecl std::vector createAreaLightsForScene(const std::shared_ptr& pScene); /** Analytic area light source. */ - class AnalyticAreaLight : public Light, public std::enable_shared_from_this + class dlldecl AnalyticAreaLight : public Light { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - static SharedPtr create(); + /** Creates an analytic area light. + \param[in] type The type of analytic area light (rectangular, sphere, disc etc). See HostDeviceSharedMacros.h + */ + static SharedPtr create(uint32_t type); - AnalyticAreaLight(); ~AnalyticAreaLight(); /** Set light source scaling \param[in] scale x,y,z scaling factors */ - void setScaling(vec3 scale) { mScaling = scale; update(); } + void setScaling(vec3 scale) { mScaling = scale; } /** Set light source scale */ vec3 getScaling() const { return mScaling; } - /** Set type of area light (rectangular, spherical etc) - */ - void setType(uint32_t type) { mData.type = type; update(); } - /** Get total light power (needed for light picking) */ float getPower() const override; @@ -400,28 +344,20 @@ namespace Falcor /** Set transform matrix \param[in] mtx object to world space transform matrix */ - void setTransformMatrix(const glm::mat4 &mtx) { mTransformMatrix = mtx; update(); } + void setTransformMatrix(const glm::mat4 &mtx) { mTransformMatrix = mtx; } /** Get transform matrix */ glm::mat4 getTransformMatrix() const { return mTransformMatrix; } - /** Set the light intensity. - \param[in] intensity Vec3 corresponding to RGB intensity - */ - void setIntensity(const glm::vec3& intensity) { mData.intensity = intensity; update(); } - /** Render UI elements for this light. \param[in] pGui The GUI to create the elements with \param[in] group Optional. If specified, creates a UI group to display elements within */ void renderUI(Gui* pGui, const char* group = nullptr) override; - /** IMovableObject interface - */ - void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override; - private: + AnalyticAreaLight(uint32_t type); void update(); bool mDirty = true; @@ -444,4 +380,6 @@ namespace Falcor return ""; } } + + enum_class_operators(Light::Changes); } diff --git a/Framework/Source/Graphics/LightProbe.cpp b/Source/Falcor/Scene/Lights/LightProbe.cpp similarity index 73% rename from Framework/Source/Graphics/LightProbe.cpp rename to Source/Falcor/Scene/Lights/LightProbe.cpp index edb46a5f7..b3b7ee698 100644 --- a/Framework/Source/Graphics/LightProbe.cpp +++ b/Source/Falcor/Scene/Lights/LightProbe.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "LightProbe.h" -#include "API/Device.h" -#include "TextureHelper.h" -#include "Utils/Gui.h" -#include "Graphics/FboHelper.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" +#include "Utils/UI/Gui.h" +#include "Core/API/RenderContext.h" +#include "Core/API/Device.h" namespace Falcor { @@ -51,9 +51,10 @@ namespace Falcor mpDFGPass = FullScreenPass::create(std::string(kShader), Program::DefineList().add("_INTEGRATE_DFG")); // Shared - mpVars = GraphicsVars::create(mpDiffuseLDPass->getProgram()->getReflector()); mpSampler = Sampler::create(Sampler::Desc().setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear)); - mpVars->getDefaultBlock()->setSampler("gSampler", mpSampler); + mpDiffuseLDPass["gSampler"] = mpSampler; + mpSpecularLDPass["gSampler"] = mpSampler; + mpDFGPass["gSampler"] = mpSampler; mInitialized = true; } @@ -63,7 +64,6 @@ namespace Falcor mpDiffuseLDPass = nullptr; mpSpecularLDPass = nullptr; mpDFGPass = nullptr; - mpVars = nullptr; mpSampler = nullptr; mInitialized = false; @@ -81,13 +81,11 @@ namespace Falcor Texture::SharedPtr integrateSpecularLD(RenderContext* pContext, const Texture::SharedPtr& pTexture, uint32_t size, ResourceFormat format, uint32_t sampleCount) { - mpVars->getDefaultBlock()->setTexture("gInputTex", pTexture); - mpVars["DataCB"]["gSampleCount"] = sampleCount; + mpSpecularLDPass["gInputTex"] = pTexture; + mpSpecularLDPass["DataCB"]["gSampleCount"] = sampleCount; Texture::SharedPtr pOutput = Texture::create2D(size, size, format, 1, Texture::kMaxPossible, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget); - GraphicsState::SharedPtr pState = pContext->getGraphicsState(); - pContext->pushGraphicsVars(mpVars); // Execute on each mip level uint32_t mipCount = pOutput->getMipCount(); for (uint32_t i = 0; i < mipCount; i++) @@ -96,44 +94,33 @@ namespace Falcor pFbo->attachColorTarget(pOutput, 0, i); // Roughness to integrate for on current mip level - mpVars["DataCB"]["gRoughness"] = float(i) / float(mipCount - 1); - - pState->pushFbo(pFbo); - mpSpecularLDPass->execute(pContext); - pState->popFbo(); + mpSpecularLDPass["DataCB"]["gRoughness"] = float(i) / float(mipCount - 1); + mpSpecularLDPass->execute(pContext, pFbo); } - pContext->popGraphicsVars(); return pOutput; } private: - Texture::SharedPtr executeSingleMip(RenderContext* pContext, const FullScreenPass::UniquePtr& pPass, const Texture::SharedPtr& pTexture, uint32_t size, ResourceFormat format, uint32_t sampleCount) + Texture::SharedPtr executeSingleMip(RenderContext* pContext, const FullScreenPass::SharedPtr& pPass, const Texture::SharedPtr& pTexture, uint32_t size, ResourceFormat format, uint32_t sampleCount) { - mpVars->getDefaultBlock()->setTexture("gInputTex", pTexture); - mpVars["DataCB"]["gSampleCount"] = sampleCount; + pPass["gInputTex"] = pTexture; + pPass["DataCB"]["gSampleCount"] = sampleCount; // Output texture - Fbo::SharedPtr pFbo = FboHelper::create2D(size, size, Fbo::Desc().setColorTarget(0, format)); + Fbo::SharedPtr pFbo = Fbo::create2D(size, size, Fbo::Desc().setColorTarget(0, format)); // Execute - GraphicsState::SharedPtr pState = pContext->getGraphicsState(); - pState->pushFbo(pFbo); - pContext->pushGraphicsVars(mpVars); - pPass->execute(pContext); - pContext->popGraphicsVars(); - pState->popFbo(); - + pPass->execute(pContext, pFbo); return pFbo->getColorTexture(0); } bool mInitialized = false; - FullScreenPass::UniquePtr mpDiffuseLDPass; - FullScreenPass::UniquePtr mpSpecularLDPass; - FullScreenPass::UniquePtr mpDFGPass; - GraphicsVars::SharedPtr mpVars; + FullScreenPass::SharedPtr mpDiffuseLDPass; + FullScreenPass::SharedPtr mpSpecularLDPass; + FullScreenPass::SharedPtr mpDFGPass; Sampler::SharedPtr mpSampler; }; @@ -173,7 +160,7 @@ namespace Falcor Texture::SharedPtr pTexture; if (overrideFormat != ResourceFormat::Unknown) { - Texture::SharedPtr pOrigTex = createTextureFromFile(filename, false, loadAsSrgb); + Texture::SharedPtr pOrigTex = Texture::createFromFile(filename, false, loadAsSrgb); pTexture = Texture::create2D(pOrigTex->getWidth(), pOrigTex->getHeight(), overrideFormat, 1, Texture::kMaxPossible, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); pTexture->setSourceFilename(pOrigTex->getSourceFilename()); gpDevice->getRenderContext()->blit(pOrigTex->getSRV(0, 1, 0, 1), pTexture->getRTV(0, 0, 1)); @@ -181,7 +168,7 @@ namespace Falcor } else { - pTexture = createTextureFromFile(filename, true, loadAsSrgb); + pTexture = Texture::createFromFile(filename, true, loadAsSrgb); } return create(pContext, pTexture, diffSampleCount, specSampleCount, diffSize, specSize, preFilteredFormat); @@ -199,30 +186,23 @@ namespace Falcor void LightProbe::renderUI(Gui* pGui, const char* group) { - if (group == nullptr || pGui->beginGroup(group)) + Gui::Group g(pGui, group); + if (!group || g.open()) { - pGui->addFloat3Var("World Position", mData.posW, -FLT_MAX, FLT_MAX); + g.var("World Position", mData.posW, -FLT_MAX, FLT_MAX); float intensity = mData.intensity.r; - if (pGui->addFloatVar("Intensity", intensity, 0.0f)) + if (g.var("Intensity", intensity, 0.0f)) { mData.intensity = vec3(intensity); } - pGui->addFloatVar("Radius", mData.radius, -1.0f); + g.var("Radius", mData.radius, -1.0f); - if (group != nullptr) - { - pGui->endGroup(); - } + if (g.open()) g.release(); } } - void LightProbe::move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) - { - logWarning("Light probes don't support paths. Expect absolutely nothing to happen"); - } - static bool checkOffset(size_t cbOffset, size_t cppOffset, const char* field) { if (cbOffset != cppOffset) @@ -239,8 +219,9 @@ namespace Falcor #define check_offset(_a) #endif - void LightProbe::setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pBuffer, const std::string& varName) + void LightProbe::setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) { + ConstantBuffer* pBuffer = pBlock->getDefaultConstantBuffer().get(); size_t offset = pBuffer->getVariableOffset(varName); // Set the data into the constant buffer @@ -250,7 +231,7 @@ namespace Falcor if (offset == ConstantBuffer::kInvalidOffset) { - logWarning("LightProbe::setIntoProgramVars() - variable \"" + varName + "\"not found in constant buffer\n"); + logWarning("LightProbe::setIntoParameterBlock() - variable \"" + varName + "\"not found in default constant buffer\n"); return; } @@ -260,15 +241,20 @@ namespace Falcor pBuffer->setBlob(&mData, offset, kDataSize); // Bind the textures - pVars->setTexture(varName + ".resources.origTexture", mData.resources.origTexture); - pVars->setTexture(varName + ".resources.diffuseTexture", mData.resources.diffuseTexture); - pVars->setTexture(varName + ".resources.specularTexture", mData.resources.specularTexture); - pVars->setSampler(varName + ".resources.sampler", mData.resources.sampler); + pBlock->setTexture(varName + ".resources.origTexture", mData.resources.origTexture); + pBlock->setTexture(varName + ".resources.diffuseTexture", mData.resources.diffuseTexture); + pBlock->setTexture(varName + ".resources.specularTexture", mData.resources.specularTexture); + pBlock->setSampler(varName + ".resources.sampler", mData.resources.sampler); + } + + void LightProbe::setSharedIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) + { + pBlock->setTexture(varName + ".dfgTexture", sSharedData.dfgTexture); + pBlock->setSampler(varName + ".dfgSampler", sSharedData.dfgSampler); } - void LightProbe::setCommonIntoProgramVars(ProgramVars* pVars, const std::string& varName) + SCRIPT_BINDING(LightProbe) { - pVars->setTexture(varName + ".dfgTexture", sSharedData.dfgTexture); - pVars->setSampler(varName + ".dfgSampler", sSharedData.dfgSampler); + m.regClass(LightProbe); } } diff --git a/Framework/Source/Graphics/LightProbe.h b/Source/Falcor/Scene/Lights/LightProbe.h similarity index 89% rename from Framework/Source/Graphics/LightProbe.h rename to Source/Falcor/Scene/Lights/LightProbe.h index 49b63d45a..8e3b85ec6 100644 --- a/Framework/Source/Graphics/LightProbe.h +++ b/Source/Falcor/Scene/Lights/LightProbe.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,23 +26,23 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Paths/MovableObject.h" -#include "API/Texture.h" #include "Data/HostDeviceData.h" -#include "API/Sampler.h" +#include "Core/API/Texture.h" +#include "Core/API/Sampler.h" namespace Falcor { + class RenderContext; + class Gui; class ProgramVars; class ConstantBuffer; - class Gui; - class LightProbe : public IMovableObject, public inherit_shared_from_this + class dlldecl LightProbe { public: using SharedPtr = std::shared_ptr; using SharedConstPtr = std::shared_ptr; - SharedPtr shared_from_this() { return inherit_shared_from_this::shared_from_this(); } + using ConstSharedPtrRef = const SharedPtr&; static const uint32_t kDataSize = sizeof(LightProbeData) - sizeof(LightProbeResources); static const uint32_t kDefaultDiffSamples = 4096; @@ -138,13 +138,15 @@ namespace Falcor */ static const Texture::SharedPtr& getDfgTexture() { return sSharedData.dfgTexture; } - /** Bind the light data into a ProgramVars object + /** Bind the light data into a Parameter Block + \param[in] varName Variable name for a LightProbeData struct. If blank, pBlock IS a Parameter Block of LightProbeData. */ - void setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pBuffer, const std::string& varName); + void setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName = ""); - /** Bind common light probe resources into a ProgramVars object + /** Bind shared light probe resources into a Parameter Block + \param[in] varName Variable name for a LightProbeSharedResources struct. If blank, pBlock IS a Parameter Block of LightProbeSharedResources. */ - static void setCommonIntoProgramVars(ProgramVars* pVars, const std::string& varName); + static void setSharedIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName = ""); private: static uint32_t sLightProbeCount; @@ -153,7 +155,6 @@ namespace Falcor LightProbeData mData; uint32_t mDiffSampleCount; uint32_t mSpecSampleCount; - void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) override; LightProbe(RenderContext* pContext, const Texture::SharedPtr& pTexture, uint32_t diffSamples, uint32_t specSamples, uint32_t diffSize, uint32_t specSize, ResourceFormat preFilteredFormat); }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Material/Material.cpp b/Source/Falcor/Scene/Material/Material.cpp similarity index 90% rename from Framework/Source/Graphics/Material/Material.cpp rename to Source/Falcor/Scene/Material/Material.cpp index 396910355..d3b881a30 100644 --- a/Framework/Source/Graphics/Material/Material.cpp +++ b/Source/Falcor/Scene/Material/Material.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,26 +25,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Material.h" -#include "API/ConstantBuffer.h" -#include "API/Texture.h" -#include "API/Buffer.h" -#include "Utils/Platform/OS.h" -#include "Utils/Math/FalcorMath.h" -#include "Graphics/Program/ProgramVars.h" -#include "Graphics/Program/GraphicsProgram.h" +#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/ProgramVars.h" namespace Falcor { - uint32_t Material::sMaterialCounter = 0; ParameterBlockReflection::SharedConstPtr Material::spBlockReflection; static const char* kMaterialVarName = "materialBlock"; Material::Material(const std::string& name) : mName(name) { - mData.id = sMaterialCounter; - sMaterialCounter++; if (spBlockReflection == nullptr) { GraphicsProgram::SharedPtr pProgram = GraphicsProgram::createFromFile("Framework/Shaders/MaterialBlock.slang", "", "main"); @@ -63,17 +55,6 @@ namespace Falcor Material::~Material() = default; - void Material::resetGlobalIdCounter() - { - sMaterialCounter = 0; - } - - void Material::setID(int32_t id) - { - mParamBlockDirty = mParamBlockDirty || (mData.id != id); - mData.id = id; - } - void Material::setShadingModel(uint32_t model) { mParamBlockDirty = mParamBlockDirty || (EXTRACT_SHADING_MODEL(mData.flags) != model); @@ -89,7 +70,7 @@ namespace Falcor void Material::setDoubleSided(bool doubleSided) { mParamBlockDirty = mParamBlockDirty || (EXTRACT_DOUBLE_SIDED(mData.flags) != doubleSided); - mData.flags = PACK_DOUBLE_SIDED(mData.flags, doubleSided ? 1 : 0); + mData.flags = PACK_DOUBLE_SIDED(mData.flags, doubleSided ? 1 : 0); } void Material::setAlphaThreshold(float alpha) @@ -160,6 +141,13 @@ namespace Falcor updateEmissiveType(); } + void Material::setEmissiveFactor(float factor) + { + mParamBlockDirty = mParamBlockDirty || (mData.emissiveFactor != factor); + mData.emissiveFactor = factor; + updateEmissiveType(); + } + template static uint32_t getChannelMode(bool hasTexture, const vec& color) { @@ -180,7 +168,7 @@ namespace Falcor void Material::updateEmissiveType() { - mData.flags = PACK_EMISSIVE_TYPE(mData.flags, getChannelMode(mData.resources.emissive != nullptr, mData.emissive)); + mData.flags = PACK_EMISSIVE_TYPE(mData.flags, getChannelMode(mData.resources.emissive != nullptr, mData.emissive * mData.emissiveFactor)); } void Material::updateOcclusionFlag() @@ -255,6 +243,7 @@ namespace Falcor compare_field(baseColor); compare_field(specular); compare_field(emissive); + compare_field(emissiveFactor); compare_field(alphaThreshold); compare_field(IoR); compare_field(flags); @@ -294,7 +283,7 @@ namespace Falcor pCB->setBlob(&data, offset, dataSize); // Now set the textures -#define set_texture(texName) pBlock->setTexture(varName + "resources." #texName, data.resources.texName) +#define set_texture(texName) pBlock->setTexture(varName + ".resources." #texName, data.resources.texName) set_texture(baseColor); set_texture(specular); set_texture(emissive); @@ -303,13 +292,14 @@ namespace Falcor set_texture(lightMap); set_texture(heightMap); #undef set_texture - pBlock->setSampler(varName + "resources.samplerState", data.resources.samplerState); + pBlock->setSampler(varName + ".resources.samplerState", data.resources.samplerState); } - void Material::setIntoParameterBlock(ParameterBlock* pBlock) const + void Material::setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName) const { ConstantBuffer* pCB = pBlock->getDefaultConstantBuffer().get(); - setMaterialIntoBlockCommon(pBlock, pCB, 0, "", mData); + size_t offset = pCB->getVariableOffset(varName); + setMaterialIntoBlockCommon(pBlock.get(), pCB, offset, varName, mData); } void Material::setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCb, const char varName[]) const @@ -321,16 +311,21 @@ namespace Falcor logError(std::string("Material::setIntoProgramVars() - variable \"") + varName + "\" not found in constant buffer\n"); return; } - setMaterialIntoBlockCommon(pVars->getDefaultBlock().get(), pCb, offset, std::string(varName) + '.', mData); + setMaterialIntoBlockCommon(pVars->getDefaultBlock().get(), pCb, offset, std::string(varName), mData); } - ParameterBlock::SharedConstPtr Material::getParameterBlock() const + const ParameterBlock::SharedPtr& Material::getParameterBlock() const { if (mParamBlockDirty) { mParamBlockDirty = false; - setIntoParameterBlock(mpParameterBlock.get()); + setIntoParameterBlock(mpParameterBlock); } return mpParameterBlock; } + + SCRIPT_BINDING(Material) + { + m.regClass(Material); + } } diff --git a/Framework/Source/Graphics/Material/Material.h b/Source/Falcor/Scene/Material/Material.h similarity index 89% rename from Framework/Source/Graphics/Material/Material.h rename to Source/Falcor/Scene/Material/Material.h index 33772ceed..39e8afdc8 100644 --- a/Framework/Source/Graphics/Material/Material.h +++ b/Source/Falcor/Scene/Material/Material.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,24 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec3.hpp" -#include -#include -#include "glm/common.hpp" -#include "glm/geometric.hpp" -#include "API/Texture.h" -#include "glm/mat4x4.hpp" -#include "API/Sampler.h" #include "Data/HostDeviceData.h" -#include "Graphics/Program/ParameterBlock.h" +#include "Core/Program/ParameterBlock.h" namespace Falcor { - class Texture; - class ProgramVars; - class ConstantBuffer; - - /** Channel Layout For Different Shading Models (Options listed in HostDeviceSharedMacros.h) @@ -54,7 +41,7 @@ namespace Falcor Specular - R - Occlusion - G - Roughness - - B - Metalness + - B - Metallic - A - Reserved ShadingModelSpecGloss @@ -73,10 +60,11 @@ namespace Falcor - 3-Channel standard normal map, or 2-Channel BC5 format */ - class Material : public std::enable_shared_from_this + class dlldecl Material : public std::enable_shared_from_this { public: using SharedPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; using SharedConstPtr = std::shared_ptr; /** Create a new material. @@ -94,18 +82,6 @@ namespace Falcor */ const std::string& getName() const { return mName; } - /** Get the material ID. - */ - const int32_t getId() const { return mData.id; } - - /** Set the material ID - */ - void setID(int32_t id); - - /** Reset all global id counter of model, mesh and material - */ - static void resetGlobalIdCounter(); - /** Set the base color texture */ void setBaseColorTexture(Texture::SharedPtr& pBaseColor); @@ -190,10 +166,18 @@ namespace Falcor */ void setEmissiveColor(const vec3& color); + /** Set the emissive factor + */ + void setEmissiveFactor(float factor); + /** Get the emissive color */ const vec3& getEmissiveColor() const { return mData.emissive; } + /** Get the emissive factor + */ + float getEmissiveFactor() const { return mData.emissiveFactor; } + /** Set the alpha mode */ void setAlphaMode(uint32_t alphaMode); @@ -245,7 +229,7 @@ namespace Falcor /** Returns true if material is emissive. */ bool isEmissive() const { return EXTRACT_EMISSIVE_TYPE(mData.flags) != ChannelTypeUnused; } - + /** Comparison operator */ bool operator==(const Material& other) const; @@ -261,16 +245,25 @@ namespace Falcor /** Bind the material to a program variables object */ void setIntoProgramVars(ProgramVars* pVars, ConstantBuffer* pCB, const char varName[]) const; - + + /** Bind the material into a Parameter Block + \param[in] varName Name of the variable containing material data. Empty string means the ParameterBlock IS the material data block. + */ + void setIntoParameterBlock(const ParameterBlock::SharedPtr& pBlock, const std::string& varName = "") const; + /** Get the ParameterBlock object for the material. Each material is created with a parameter-block. Using it is more efficient than assigning data to a custom constant-buffer. */ - ParameterBlock::SharedConstPtr getParameterBlock() const; + const ParameterBlock::SharedPtr& getParameterBlock() const; + + /** Returns the material data struct. + */ + const MaterialData& getData() const { return mData; } + private: void updateBaseColorType(); void updateSpecularType(); void updateEmissiveType(); void updateOcclusionFlag(); - void setIntoParameterBlock(ParameterBlock* pBlock) const; Material(const std::string& name); std::string mName; @@ -278,7 +271,6 @@ namespace Falcor bool mOcclusionMapEnabled = false; mutable bool mParamBlockDirty = true; ParameterBlock::SharedPtr mpParameterBlock; - static uint32_t sMaterialCounter; static ParameterBlockReflection::SharedConstPtr spBlockReflection; }; diff --git a/Framework/Source/Effects/ParticleSystem/ParticleSystem.cpp b/Source/Falcor/Scene/ParticleSystem/ParticleSystem.cpp similarity index 77% rename from Framework/Source/Effects/ParticleSystem/ParticleSystem.cpp rename to Source/Falcor/Scene/ParticleSystem/ParticleSystem.cpp index 6e7d06b83..518bb9a28 100644 --- a/Framework/Source/Effects/ParticleSystem/ParticleSystem.cpp +++ b/Source/Falcor/Scene/ParticleSystem/ParticleSystem.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" #include "ParticleSystem.h" -#include "API/Resource.h" +#include "Core/API/RenderContext.h" +#include "Utils/UI/Gui.h" #include "glm/gtc/random.hpp" -#include -#include "Utils/Gui.h" -#include namespace Falcor { @@ -85,16 +84,16 @@ namespace Falcor GraphicsProgram::SharedPtr pDrawProgram = GraphicsProgram::create(d, defineList); //ParticlePool - mpParticlePool = StructuredBuffer::create(pEmitCs, "particlePool", mMaxParticles); + mpParticlePool = StructuredBuffer::create(pEmitCs.get(), "particlePool", mMaxParticles); //emitList - mpEmitList = StructuredBuffer::create(pEmitCs, "emitList", mMaxEmitPerFrame); + mpEmitList = StructuredBuffer::create(pEmitCs.get(), "emitList", mMaxEmitPerFrame); //Dead List - mpDeadList = StructuredBuffer::create(pEmitCs, "deadList", mMaxParticles); + mpDeadList = StructuredBuffer::create(pEmitCs.get(), "deadList", mMaxParticles); // Init data in dead list buffer - mpDeadList->getUAVCounter()->updateData(&mMaxParticles, 0, sizeof(uint32_t)); + mpDeadList->getUAVCounter()->setBlob(&mMaxParticles, 0, sizeof(uint32_t)); std::vector indices; indices.resize(mMaxParticles); uint32_t counter = 0; @@ -102,11 +101,11 @@ namespace Falcor mpDeadList->setBlob(indices.data(), 0, indices.size() * sizeof(uint32_t)); // Alive list - mpAliveList = StructuredBuffer::create(pSimulateCs, "aliveList", mMaxParticles); + mpAliveList = StructuredBuffer::create(pSimulateCs.get(), "aliveList", mMaxParticles); // Indirect args Resource::BindFlags indirectBindFlags = Resource::BindFlags::IndirectArg | Resource::BindFlags::UnorderedAccess; - mpIndirectArgs = StructuredBuffer::create(pSimulateCs, "drawArgs", 1, indirectBindFlags); + mpIndirectArgs = StructuredBuffer::create(pSimulateCs.get(), "drawArgs", 1, indirectBindFlags); //initialize the first member of the args, vert count per instance, to be 4 for particle billboards uint32_t vertexCountPerInstance = 4; @@ -185,13 +184,9 @@ namespace Falcor mpEmitList->setBlob(emittedParticles.data(), 0, emittedParticles.size() * sizeof(Particle)); //Send vars and call - pCtx->pushComputeState(mEmitResources.pState); mEmitResources.pVars->getDefaultBlock()->getConstantBuffer(mBindLocations.emitCB, 0)->setBlob(&emitData, 0u, sizeof(EmitData)); - pCtx->pushComputeVars(mEmitResources.pVars); - uint32_t numGroups = (uint32_t)std::ceil((float)num / EMIT_THREADS); - pCtx->dispatch(1, numGroups, 1); - pCtx->popComputeVars(); - pCtx->popComputeState(); + uint32_t numGroups = div_round_up(num, (uint32_t)EMIT_THREADS); + pCtx->dispatch(mEmitResources.pState.get(), mEmitResources.pVars.get(), {1, numGroups, 1}); } void ParticleSystem::update(RenderContext* pCtx, float dt, glm::mat4 view) @@ -224,28 +219,14 @@ namespace Falcor //reset alive list counter to 0 uint32_t zero = 0; - mpAliveList->getUAVCounter()->updateData(&zero, 0, sizeof(uint32_t)); - - pCtx->pushComputeState(mSimulateResources.pState); - pCtx->pushComputeVars(mSimulateResources.pVars); - pCtx->dispatch(max(mMaxParticles / mSimulateThreads, 1u), 1, 1); - pCtx->popComputeVars(); - pCtx->popComputeState(); + mpAliveList->getUAVCounter()->setBlob(&zero, 0, sizeof(uint32_t)); + pCtx->dispatch(mSimulateResources.pState.get(), mSimulateResources.pVars.get(), {max(mMaxParticles / mSimulateThreads, 1u), 1, 1}); } - void ParticleSystem::render(RenderContext* pCtx, glm::mat4 view, glm::mat4 proj) + void ParticleSystem::render(RenderContext* pCtx, const Fbo::SharedPtr& pDst, glm::mat4 view, glm::mat4 proj) { //sorting - if (mShouldSort) - { - pCtx->pushComputeState(mSortResources.pState); - pCtx->pushComputeVars(mSortResources.pVars); - - pCtx->dispatch(1, 1, 1); - - pCtx->popComputeVars(); - pCtx->popComputeState(); - } + if (mShouldSort) pCtx->dispatch(mSortResources.pState.get(), mSortResources.pVars.get(), {1, 1, 1}); //Draw cbuf VSPerFrame cbuf; @@ -254,43 +235,40 @@ namespace Falcor mDrawResources.pVars->getDefaultBlock()->getConstantBuffer(mBindLocations.drawCB, 0)->setBlob(&cbuf, 0, sizeof(cbuf)); //particle draw uses many of render context's existing state's properties - GraphicsState::SharedPtr state = pCtx->getGraphicsState(); - mDrawResources.pState->setBlendState(state->getBlendState()); - mDrawResources.pState->setFbo(state->getFbo()); - mDrawResources.pState->setRasterizerState(state->getRasterizerState()); - mDrawResources.pState->setDepthStencilState(state->getDepthStencilState()); - - pCtx->pushGraphicsState(mDrawResources.pState); - pCtx->pushGraphicsVars(mDrawResources.pVars); - pCtx->drawIndirect(mpIndirectArgs.get(), 0); - pCtx->popGraphicsVars(); - pCtx->popGraphicsState(); + mDrawResources.pState->setFbo(pDst); + pCtx->drawIndirect(mDrawResources.pState.get(), mDrawResources.pVars.get(), 1, mpIndirectArgs.get(), 0, nullptr, 0); } - void ParticleSystem::renderUi(Gui* pGui) + void ParticleSystem::renderUi(Gui::Widgets& widget) { - float floatMax = std::numeric_limits::max(); - pGui->addFloatVar("Duration", mEmitter.duration, 0.f); - pGui->addFloatVar("DurationOffset", mEmitter.durationOffset, 0.f); - pGui->addFloatVar("Frequency", mEmitter.emitFrequency, 0.01f); - int32_t emitCount = mEmitter.emitCount; - pGui->addIntVar("EmitCount", emitCount, 0, mMaxEmitPerFrame); - mEmitter.emitCount = emitCount; - pGui->addIntVar("EmitCountOffset", mEmitter.emitCountOffset, 0); - pGui->addFloat3Var("SpawnPos", mEmitter.spawnPos, -floatMax, floatMax); - pGui->addFloat3Var("SpawnPosOffset", mEmitter.spawnPosOffset, 0.f, floatMax); - pGui->addFloat3Var("Velocity", mEmitter.vel, -floatMax, floatMax); - pGui->addFloat3Var("VelOffset", mEmitter.velOffset, 0.f, floatMax); - pGui->addFloat3Var("Accel", mEmitter.accel, -floatMax, floatMax); - pGui->addFloat3Var("AccelOffset", mEmitter.accelOffset, 0.f, floatMax); - pGui->addFloatVar("Scale", mEmitter.scale, 0.001f); - pGui->addFloatVar("ScaleOffset", mEmitter.scaleOffset, 0.001f); - pGui->addFloatVar("Growth", mEmitter.growth); - pGui->addFloatVar("GrowthOffset", mEmitter.growthOffset, 0.001f); - pGui->addFloatVar("BillboardRotation", mEmitter.billboardRotation); - pGui->addFloatVar("BillboardRotationOffset", mEmitter.billboardRotationOffset); - pGui->addFloatVar("BillboardRotationVel", mEmitter.billboardRotationVel); - pGui->addFloatVar("BillboardRotationVelOffset", mEmitter.billboardRotationVelOffset); + auto g = Gui::Group(widget, "Particle System Settings"); + if (g.open()) + { + float floatMax = std::numeric_limits::max(); + g.var("Duration", mEmitter.duration, 0.f); + g.var("DurationOffset", mEmitter.durationOffset, 0.f); + g.var("Frequency", mEmitter.emitFrequency, 0.01f); + int32_t emitCount = mEmitter.emitCount; + g.var("EmitCount", emitCount, 0, static_cast(mMaxEmitPerFrame)); + mEmitter.emitCount = emitCount; + g.var("EmitCountOffset", mEmitter.emitCountOffset, 0); + g.var("SpawnPos", mEmitter.spawnPos, -floatMax, floatMax); + g.var("SpawnPosOffset", mEmitter.spawnPosOffset, 0.f, floatMax); + g.var("Velocity", mEmitter.vel, -floatMax, floatMax); + g.var("VelOffset", mEmitter.velOffset, 0.f, floatMax); + g.var("Accel", mEmitter.accel, -floatMax, floatMax); + g.var("AccelOffset", mEmitter.accelOffset, 0.f, floatMax); + g.var("Scale", mEmitter.scale, 0.001f); + g.var("ScaleOffset", mEmitter.scaleOffset, 0.001f); + g.var("Growth", mEmitter.growth); + g.var("GrowthOffset", mEmitter.growthOffset, 0.001f); + g.var("BillboardRotation", mEmitter.billboardRotation); + g.var("BillboardRotationOffset", mEmitter.billboardRotationOffset); + g.var("BillboardRotationVel", mEmitter.billboardRotationVel); + g.var("BillboardRotationVelOffset", mEmitter.billboardRotationVelOffset); + + g.release(); + } } void ParticleSystem::initSortResources() @@ -299,7 +277,7 @@ namespace Falcor ComputeProgram::SharedPtr pSortCs = ComputeProgram::createFromFile(kSortShader, "main"); //iteration counter buffer - mSortResources.pSortIterationCounter = StructuredBuffer::create(pSortCs, "iterationCounter", 2); + mSortResources.pSortIterationCounter = StructuredBuffer::create(pSortCs.get(), "iterationCounter", 2); //Sort data reset buffer SortData resetData; @@ -367,4 +345,9 @@ namespace Falcor mEmitter.billboardRotationVel = rotVel; mEmitter.billboardRotationVelOffset = offset; } + + std::shared_ptr ParticleSystem::getSimulateProgram() const + { + return mSimulateResources.pState->getProgram(); + } } diff --git a/Framework/Source/Effects/ParticleSystem/ParticleSystem.h b/Source/Falcor/Scene/ParticleSystem/ParticleSystem.h similarity index 95% rename from Framework/Source/Effects/ParticleSystem/ParticleSystem.h rename to Source/Falcor/Scene/ParticleSystem/ParticleSystem.h index c0a7e99a0..39f4e4a15 100644 --- a/Framework/Source/Effects/ParticleSystem/ParticleSystem.h +++ b/Source/Falcor/Scene/ParticleSystem/ParticleSystem.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,16 +26,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - -#include "Framework.h" -#include "API/RenderContext.h" +#include "Core/Program/ProgramVars.h" +#include "Core/Program/ComputeProgram.h" +#include "Core/State/ComputeState.h" +#include "Core/State/GraphicsState.h" +#include "Core/Program/GraphicsProgram.h" #include "Data/Effects/ParticleData.h" namespace Falcor { - class Gui; - - class ParticleSystem + class dlldecl ParticleSystem { public: static const char* kVertexShader; ///< Filename for the vertex shader @@ -64,12 +64,12 @@ namespace Falcor /** Render the particle system, sorting if necessary and drawing the particles */ - void render(RenderContext* pCtx, glm::mat4 view, glm::mat4 proj); + void render(RenderContext* pCtx, const Fbo::SharedPtr& pDst, glm::mat4 view, glm::mat4 proj); /** Render UI controls for this particle system. \param[in] pGui GUI instance to render UI elements with */ - void renderUi(Gui* pGui); + void renderUi(Gui::Widgets& widget); /** Gets the graphics vars for drawing. */ @@ -77,7 +77,7 @@ namespace Falcor /** Gets the simulation shader program */ - ComputeProgram::SharedPtr getSimulateProgram() { return mSimulateResources.pState->getProgram(); } + ComputeProgram::SharedPtr getSimulateProgram() const; /** Get the graphics vars used for the particle simulation shader */ diff --git a/Source/Falcor/Scene/Scene.cpp b/Source/Falcor/Scene/Scene.cpp new file mode 100644 index 000000000..6a6c9b6cb --- /dev/null +++ b/Source/Falcor/Scene/Scene.cpp @@ -0,0 +1,950 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Scene.h" +#include "Raytracing/RtState.h" +#include "Raytracing/RtProgramVars.h" + +namespace Falcor +{ + namespace + { + // Checks if the transform flips the coordinate system handedness (its determinant is negative). + bool doesTransformFlip(const mat4& m) + { + return determinant((mat3)m) < 0.f; + } + + const std::string kParameterBlockName = "gScene"; + const std::string kMeshBufferName = "meshes"; + const std::string kMeshInstanceBufferName = "meshInstances"; + const std::string kIndexBufferName = "indices"; + const std::string kVertexBufferName = "vertices"; + const std::string kLightsBufferName = "lights"; + const std::string kCameraVarName = "camera"; + } + + const FileDialogFilterVec Scene::kFileExtensionFilters = + { + {"fscene"}, + {"fbx"}, + {"gltf"}, + {"obj"}, + {"dae"}, + {"x"}, + {"md5mesh"}, + {"ply"}, + {"3ds"}, + {"blend"}, + {"ase"}, + {"ifc"}, + {"xgl"}, + {"zgl"}, + {"dxf"}, + {"lwo"}, + {"lws"}, + {"lxo"}, + {"stl"}, + {"x"}, + {"ac"}, + {"ms3d"}, + {"cob"}, + {"scn"}, + {"3d"}, + {"mdl"}, + {"mdl2"}, + {"pk3"}, + {"smd"}, + {"vta"}, + {"raw"}, + {"ter"}, + {"glb"} + }; + + Scene::Scene() + { + mpFrontClockwiseRS = RasterizerState::create(RasterizerState::Desc().setFrontCounterCW(false)); + mpNoCullRS = RasterizerState::create(RasterizerState::Desc().setCullMode(RasterizerState::CullMode::None)); + } + + Scene::SharedPtr Scene::create(const std::string& filename) + { + auto pBuilder = SceneBuilder::create(filename); + return pBuilder ? pBuilder->getScene() : nullptr; + } + + Scene::SharedPtr Scene::create() + { + return Scene::SharedPtr(new Scene()); + } + + Shader::DefineList Scene::getSceneDefines() + { + Shader::DefineList defines; + defines.add("MATERIAL_COUNT", std::to_string(mMaterials.size())); + return defines; + } + + void Scene::render(RenderContext* pContext, GraphicsState* pState, GraphicsVars* pVars, RenderFlags flags) + { + PROFILE("renderScene"); + + pState->setVao(mpVao); + pVars->setParameterBlock("gScene", mpSceneBlock); + + bool overrideRS = !is_set(flags, RenderFlags::UserRasterizerState); + auto pCurrentRS = pState->getRasterizerState(); + + if (mDrawCounterClockwiseMeshes.count) + { + if (overrideRS) pState->setRasterizerState(nullptr); + pContext->drawIndexedIndirect(pState, pVars, mDrawCounterClockwiseMeshes.count, mDrawCounterClockwiseMeshes.pBuffer.get(), 0, nullptr, 0); + } + + if (mDrawClockwiseMeshes.count) + { + if (overrideRS) pState->setRasterizerState(mpFrontClockwiseRS); + pContext->drawIndexedIndirect(pState, pVars, mDrawClockwiseMeshes.count, mDrawClockwiseMeshes.pBuffer.get(), 0, nullptr, 0); + } + + if (mDrawAlphaTestedMeshes.count) + { + if (overrideRS) pState->setRasterizerState(mpNoCullRS); + pContext->drawIndexedIndirect(pState, pVars, mDrawAlphaTestedMeshes.count, mDrawAlphaTestedMeshes.pBuffer.get(), 0, nullptr, 0); + } + + if (overrideRS) pState->setRasterizerState(pCurrentRS); + } + + void Scene::raytrace(RenderContext* pContext, const std::shared_ptr& pState, const std::shared_ptr& pRtVars, uvec3 dispatchDims) + { + // On first execution, create BLAS for each mesh + if (mBlasData.empty()) + { + initGeomDesc(); + buildBlas(pContext); + updateAsToInstanceDataMapping(); + } + + // If not set yet, set geometry indices for this RtProgramVars + if (mRtVarsWithGeometryIndex.count(pRtVars.get()) == 0) + { + setGeometryIndexIntoRtVars(pRtVars); + mRtVarsWithGeometryIndex.insert(pRtVars.get()); + } + + // On first execution, when meshes have moved, when there's a new ray count, or when a BLAS has changed, create/update the TLAS + auto tlasIt = mTlasCache.find(pRtVars->getHitProgramsCount()); + if (tlasIt == mTlasCache.end()) + { + // We need a hit entry per mesh right now to pass GeometryIndex() + assert(pRtVars->hasPerMeshHitEntry()); + buildTlas(pContext, pRtVars->getHitProgramsCount(), true); + + // If new TLAS was just created, get it so the iterator is valid + if (tlasIt == mTlasCache.end()) tlasIt = mTlasCache.find(pRtVars->getHitProgramsCount()); + } + + { + PROFILE("applyRtVars"); + // Bind Scene Param Block + const GraphicsVars::SharedPtr& pGlobalVars = pRtVars->getGlobalVars(); + mCamera.pObject->setIntoConstantBuffer(mpSceneBlock->getDefaultConstantBuffer().get(), kCameraVarName); + pGlobalVars->setParameterBlock("gScene", mpSceneBlock); + + // Bind TLAS + ParameterBlockReflection::BindLocation loc = pGlobalVars->getReflection()->getDefaultParameterBlock()->getResourceBinding("gRtScene"); + if (loc.setIndex != ProgramReflection::kInvalidLocation) + { + pGlobalVars->getDefaultBlock()->setSrv(loc, 0, tlasIt->second.pSrv); + } + + // Nothing to set? Materials, etc is global in the param block already + // Set miss-shader data + pGlobalVars["DxrPerFrame"]["hitProgramCount"] = pRtVars->getHitProgramsCount(); + pGlobalVars->setRawBuffer("gAsToInstance", mpAsToInstanceMapping); + + if (!pRtVars->apply(pContext, pState->getRtso().get())) + { + logError("Scene::raytrace() - applying RtProgramVars failed, most likely because we ran out of descriptors."); + assert(false); + + } + } + + PROFILE("raytrace"); + pContext->raytrace(pRtVars, pState, dispatchDims.x, dispatchDims.y, dispatchDims.z); + } + + void Scene::initResources() + { + GraphicsProgram::SharedPtr pProgram = GraphicsProgram::createFromFile("Framework/Shaders/SceneBlock.slang", "", "main"); + pProgram->addDefines(getSceneDefines()); + ParameterBlockReflection::SharedConstPtr pReflection = pProgram->getReflector()->getParameterBlock(kParameterBlockName); + assert(pReflection); + + mpSceneBlock = ParameterBlock::create(pReflection, true); + + ReflectionVar::SharedConstPtr pMeshRefl = pReflection->getResource(kMeshBufferName); + mpMeshesBuffer = StructuredBuffer::create(pMeshRefl->getName(), std::dynamic_pointer_cast(pMeshRefl->getType()), mMeshDesc.size(), Resource::BindFlags::ShaderResource); + + ReflectionVar::SharedConstPtr pInstRefl = pReflection->getResource(kMeshInstanceBufferName); + mpMeshInstancesBuffer = StructuredBuffer::create(pInstRefl->getName(), std::dynamic_pointer_cast(pInstRefl->getType()), mMeshInstanceData.size(), Resource::BindFlags::ShaderResource); + + if(mLights.size()) + { + ReflectionVar::SharedConstPtr pLightsRefl = pReflection->getResource(kLightsBufferName); + mpLightsBuffer = StructuredBuffer::create(pLightsRefl->getName(), std::dynamic_pointer_cast(pLightsRefl->getType()), mLights.size(), Resource::BindFlags::ShaderResource); + } + } + + void Scene::uploadResources() + { + // Upload geometry + checkOffsets(); + mpMeshesBuffer->setBlob(mMeshDesc.data(), 0, sizeof(MeshDesc) * mMeshDesc.size()); + mpMeshInstancesBuffer->setBlob(mMeshInstanceData.data(), 0, sizeof(MeshInstanceData) * mMeshInstanceData.size()); + + mpSceneBlock->setStructuredBuffer(kMeshInstanceBufferName, mpMeshInstancesBuffer); + mpSceneBlock->setStructuredBuffer(kMeshBufferName, mpMeshesBuffer); + mpSceneBlock->setStructuredBuffer(kLightsBufferName, mpLightsBuffer); + mpSceneBlock->setRawBuffer(kIndexBufferName, mpVao->getIndexBuffer()); + mpSceneBlock->setStructuredBuffer(kVertexBufferName, mpVao->getVertexBuffer(Scene::kStaticDataBufferIndex)->asStructuredBuffer()); + + // Set material data + for (uint32_t i = 0; i < (uint32_t)mMaterials.size(); i++) + { + mMaterials[i]->setIntoParameterBlock(mpSceneBlock, "materials[" + std::to_string(i) + "]"); + } + + if (mpLightProbe) + { + LightProbe::setSharedIntoParameterBlock(mpSceneBlock, "probeShared"); + mpLightProbe->setIntoParameterBlock(mpSceneBlock, "lightProbe"); + } + } + + void Scene::checkOffsets() + { + // Reflector, Struct type, Member +#define assert_offset(_r, _s, _m) assert(_r->getOffsetDesc(offsetof(_s, _m)).type != ReflectionBasicType::Type::Unknown) + + // MeshDesc + auto pMeshReflector = mpMeshesBuffer->getBufferReflector(); + assert_offset(pMeshReflector, MeshDesc, vbOffset); + assert_offset(pMeshReflector, MeshDesc, ibOffset); + assert_offset(pMeshReflector, MeshDesc, vertexCount); + assert_offset(pMeshReflector, MeshDesc, indexCount); + assert_offset(pMeshReflector, MeshDesc, materialID); + + // MeshInstanceData + auto pInstanceReflector = mpMeshInstancesBuffer->getBufferReflector(); + assert_offset(pInstanceReflector, MeshInstanceData, meshID); + assert_offset(pInstanceReflector, MeshInstanceData, globalMatrixID); + +#undef assert_offset + } + + void Scene::updateBounds() + { + const auto& globalMatrices = mpAnimationController->getGlobalMatrices(); + std::vector instanceBBs; + instanceBBs.reserve(mMeshInstanceData.size()); + + for (const auto& inst : mMeshInstanceData) + { + const BoundingBox& meshBB = mMeshBBs[inst.meshID]; + const mat4& transform = globalMatrices[inst.globalMatrixID]; + instanceBBs.push_back(meshBB.transform(transform)); + } + + mSceneBB = instanceBBs.front(); + for (const BoundingBox& bb : instanceBBs) + { + mSceneBB = BoundingBox::fromUnion(mSceneBB, bb); + } + } + + void Scene::updateMeshInstanceFlags() + { + for (auto& inst : mMeshInstanceData) + { + inst.flags = MeshInstanceFlags::None; + + const mat4& transform = mpAnimationController->getGlobalMatrices()[inst.globalMatrixID]; + if (doesTransformFlip(transform)) inst.flags |= MeshInstanceFlags::Flipped; + } + } + + void Scene::finalize() + { + // Create mapping of meshes to their instances. + mMeshIdToInstanceIds.clear(); + mMeshIdToInstanceIds.resize(mMeshDesc.size()); + for (uint32_t i = 0; i < (uint32_t)mMeshInstanceData.size(); i++) + { + mMeshIdToInstanceIds[mMeshInstanceData[i].meshID].push_back(i); + } + + initResources(); + mpAnimationController->animate(gpDevice->getRenderContext(), 0); // Requires Scene block to exist + updateMeshInstanceFlags(); + updateBounds(); + createDrawList(); + if (mCamera.pObject == nullptr) + { + mCamera.pObject = Camera::create(); + resetCamera(); + } + setCameraController(mCamCtrlType); + updateCamera(true); + updateLights(true); + uploadResources(); // Upload data after initialization is complete + + if (mpAnimationController->getMeshAnimationCount(0)) mpAnimationController->setActiveAnimation(0, 0); + } + + template<> + void Scene::AnimatedObject::setIntoObject(const vec3& pos, const vec3& up, const vec3& lookAt) + { + pObject->setUpVector(up); + pObject->setPosition(pos); + pObject->setTarget(pos + lookAt); + } + + template<> + void Scene::AnimatedObject::setIntoObject(const vec3& pos, const vec3& up, const vec3& lookAt) + { + DirectionalLight* pDirLight = dynamic_cast(pObject.get()); + if (pDirLight) + { + pDirLight->setWorldDirection(lookAt); + return; + } + PointLight* pPointLight = dynamic_cast(pObject.get()); + if (pPointLight) + { + pPointLight->setWorldPosition(pos); + pPointLight->setWorldDirection(lookAt); + } + } + + template + bool Scene::AnimatedObject::enabled(bool force) const + { + return (animate || force) && (nodeID != kInvalidNode); + } + + template + bool Scene::AnimatedObject::update(const AnimationController* pAnimCtrl, bool force) + { + bool update = (force || animate) && nodeID != kInvalidNode; + if (update) + { + if (!pAnimCtrl->didMatrixChanged(nodeID) && !force) return false; + + mat4 camMat = pAnimCtrl->getGlobalMatrices()[nodeID]; + vec3 pos = vec3(camMat[3]); + vec3 up = vec3(camMat[1]); + vec3 lookAt = vec3(camMat[2]); + setIntoObject(pos, up, lookAt); + return true; + } + return false; + } + + Scene::UpdateFlags Scene::updateLights(bool forceUpdate) + { + UpdateFlags flags = UpdateFlags::None; + + for (size_t i = 0 ; i < mLights.size() ; i++) + { + auto& l = mLights[i]; + l.update(mpAnimationController.get(), forceUpdate); + auto lightChanges = l.pObject->beginFrame(); + + if (lightChanges != Light::Changes::None) + { + l.pObject->setIntoVariableBuffer(mpLightsBuffer.get(), sizeof(LightData)*i); + if (is_set(lightChanges, Light::Changes::Intensity)) flags |= UpdateFlags::LightIntensityChanged; + if (is_set(lightChanges, Light::Changes::Position)) flags |= UpdateFlags::LightsMoved; + if (is_set(lightChanges, Light::Changes::Direction)) flags |= UpdateFlags::LightsMoved; + const Light::Changes otherChanges = ~(Light::Changes::Intensity | Light::Changes::Position | Light::Changes::Direction); + if ((lightChanges & otherChanges) != Light::Changes::None) flags |= UpdateFlags::LightPropertiesChanged; + } + } + return flags; + } + + Scene::UpdateFlags Scene::updateCamera(bool force) + { + UpdateFlags flags = UpdateFlags::None; + mCamera.enabled(force) ? mCamera.update(mpAnimationController.get(), force) : mpCamCtrl->update(); + auto cameraChanges = mCamera.pObject->beginFrame(); + if (cameraChanges != Camera::Changes::None) + { + mCamera.pObject->setIntoConstantBuffer(mpSceneBlock->getDefaultConstantBuffer().get(), kCameraVarName); + if (is_set(cameraChanges, Camera::Changes::Movement)) flags |= UpdateFlags::CameraMoved; + if ((cameraChanges & (~Camera::Changes::Movement)) != Camera::Changes::None) flags |= UpdateFlags::CameraPropertiesChanged; + } + return flags; + } + + Scene::UpdateFlags Scene::update(RenderContext* pContext, double currentTime) + { + mUpdates = UpdateFlags::None; + if (mpAnimationController->animate(pContext, currentTime)) + { + mUpdates |= UpdateFlags::SceneGraphChanged; + for (const auto& inst : mMeshInstanceData) + { + if (mpAnimationController->didMatrixChanged(inst.globalMatrixID)) + { + mUpdates |= UpdateFlags::MeshesMoved; + } + } + } + + mUpdates |= updateCamera(false); + mUpdates |= updateLights(false); + pContext->flush(); + if (is_set(mUpdates, UpdateFlags::MeshesMoved)) + { + mTlasCache.clear(); + updateMeshInstanceFlags(); + } + + // If a transform in the scene changed, update BLASes with skinned meshes + if (mBlasData.size() && mHasSkinnedMesh && is_set(mUpdates, UpdateFlags::SceneGraphChanged)) + { + mTlasCache.clear(); + buildBlas(pContext); + } + + return mUpdates; + } + + void Scene::renderUI(Gui::Widgets& widget) + { + mpAnimationController->renderUI(widget); + if(mCamera.hasGlobalTransform()) widget.checkbox("Animate Camera", mCamera.animate); + + auto cameraGroup = Gui::Group(widget, "Camera"); + if (cameraGroup.open()) + { + if (cameraGroup.var("Camera Speed", mCameraSpeed, 0.f, FLT_MAX, 0.01f)) + { + mpCamCtrl->setCameraSpeed(mCameraSpeed); + } + mCamera.pObject->renderUI(cameraGroup.gui()); + + cameraGroup.release(); + } + + auto lightsGroup = Gui::Group(widget, "Lights"); + if (lightsGroup.open()) + { + if (mLights.size() && lightsGroup.open()) + { + for (auto& light : mLights) + { + auto name = light.pObject->getName(); + auto g = Gui::Group(widget, name); + if (g.open()) + { + if (light.hasGlobalTransform()) g.checkbox(("Animate##" + light.pObject->getName()).c_str(), light.animate); + light.pObject->renderUI(g.gui()); + g.release(); + } + } + } + + lightsGroup.release(); + } + + // Filtering mode + // Camera controller + } + + void Scene::resetCamera(bool resetDepthRange) + { + float radius = length(mSceneBB.extent); + mCamera.pObject->setPosition(mSceneBB.center); + mCamera.pObject->setTarget(mSceneBB.center + vec3(0, 0, -1)); + mCamera.pObject->setUpVector(glm::vec3(0, 1, 0)); + + if(resetDepthRange) + { + float nearZ = std::max(0.1f, radius / 750.0f); + float farZ = radius * 50; + mCamera.pObject->setDepthRange(nearZ, farZ); + } + } + + void Scene::createDrawList() + { + std::vector drawClockwiseMeshes, drawCounterClockwiseMeshes, drawAlphaTestedMeshes; + auto pMatricesBuffer = mpSceneBlock->getTypedBuffer("worldMatrices"); + const mat4* matrices = (mat4*)pMatricesBuffer->getData(); + + for (const auto& instance : mMeshInstanceData) + { + const auto& mesh = mMeshDesc[instance.meshID]; + const auto& transform = matrices[instance.globalMatrixID]; + + D3D12_DRAW_INDEXED_ARGUMENTS draw; + draw.IndexCountPerInstance = mesh.indexCount; + draw.InstanceCount = 1; + draw.StartIndexLocation = mesh.ibOffset; + draw.BaseVertexLocation = mesh.vbOffset; + draw.StartInstanceLocation = (uint32_t)(drawClockwiseMeshes.size() + drawCounterClockwiseMeshes.size() + drawAlphaTestedMeshes.size()); + + if (mMaterials[mesh.materialID]->getAlphaMode() == AlphaModeMask) + { + drawAlphaTestedMeshes.push_back(draw); + } + else + { + (doesTransformFlip(transform)) ? drawClockwiseMeshes.push_back(draw) : drawCounterClockwiseMeshes.push_back(draw); + } + } + + // Create the draw-indirect buffer + if (drawCounterClockwiseMeshes.size()) + { + mDrawCounterClockwiseMeshes.pBuffer = Buffer::create(sizeof(drawCounterClockwiseMeshes[0]) * drawCounterClockwiseMeshes.size(), Resource::BindFlags::IndirectArg, Buffer::CpuAccess::None, drawCounterClockwiseMeshes.data()); + mDrawCounterClockwiseMeshes.count = (uint32_t)drawCounterClockwiseMeshes.size(); + } + + if (drawClockwiseMeshes.size()) + { + mDrawClockwiseMeshes.pBuffer = Buffer::create(sizeof(drawClockwiseMeshes[0]) * drawClockwiseMeshes.size(), Resource::BindFlags::IndirectArg, Buffer::CpuAccess::None, drawClockwiseMeshes.data()); + mDrawClockwiseMeshes.count = (uint32_t)drawClockwiseMeshes.size(); + } + + if (drawAlphaTestedMeshes.size()) + { + mDrawAlphaTestedMeshes.pBuffer = Buffer::create(sizeof(drawAlphaTestedMeshes[0]) * drawAlphaTestedMeshes.size(), Resource::BindFlags::IndirectArg, Buffer::CpuAccess::None, drawAlphaTestedMeshes.data()); + mDrawAlphaTestedMeshes.count = (uint32_t)drawAlphaTestedMeshes.size(); + } + + size_t drawCount = drawClockwiseMeshes.size() + drawCounterClockwiseMeshes.size() + drawAlphaTestedMeshes.size(); + assert(drawCount <= UINT32_MAX); + } + + void Scene::sortBlasMeshes() + { + // Of the non-instanced meshes, group based on what global matrix ID their transform is. + std::unordered_map> nodeToMeshList; + for (uint32_t meshId = 0; meshId < (uint32_t)mMeshIdToInstanceIds.size(); meshId++) + { + auto& instanceList = mMeshIdToInstanceIds[meshId]; + if (instanceList.size() > 1) continue; // Only processing non-instanced meshes here + + uint32_t globalMatrixId = mMeshInstanceData[instanceList[0]].globalMatrixID; + nodeToMeshList[globalMatrixId].push_back(meshId); + } + + // This should currently only be run on scene initialization + assert(mBlasData.empty()); + + // Build final result. Format is a list of Mesh ID's per BLAS + + // Non-instanced meshes were sorted above so just copy each list + for (auto& it : nodeToMeshList) mBlasData.push_back(it.second); + + // Meshes that have multiple instances go in their own BLAS + for (uint32_t meshId = 0; meshId < (uint32_t)mMeshIdToInstanceIds.size(); meshId++) + { + auto& instanceList = mMeshIdToInstanceIds[meshId]; + if (instanceList.size() == 1) continue; // Only processing instanced meshes here + mBlasData.push_back(std::vector({ meshId })); + } + } + + void Scene::initGeomDesc() + { + sortBlasMeshes(); + + const VertexBufferLayout::SharedConstPtr& pVbLayout = mpVao->getVertexLayout()->getBufferLayout(kStaticDataBufferIndex); + const Buffer::SharedPtr& pVb = mpVao->getVertexBuffer(kStaticDataBufferIndex); + const Buffer::SharedPtr& pIb = mpVao->getIndexBuffer(); + + for (uint32_t i = 0; i < (uint32_t)mBlasData.size(); i++) + { + auto& blas = mBlasData[i]; + auto& meshList = blas.meshList; + auto& geomDescs = blas.geomDescs; + geomDescs.resize(meshList.size()); + + for (uint32_t j = 0; j < (uint32_t)meshList.size(); j++) + { + const MeshDesc& mesh = mMeshDesc[meshList[j]]; + blas.hasSkinnedMesh |= mMeshHasDynamicData[meshList[j]]; + + D3D12_RAYTRACING_GEOMETRY_DESC& desc = geomDescs[j]; + desc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + desc.Triangles.Transform3x4 = 0; + // If this is an opaque mesh, set the opaque flag + desc.Flags = (mMaterials[mesh.materialID]->getAlphaMode() == AlphaModeOpaque) ? D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE : D3D12_RAYTRACING_GEOMETRY_FLAG_NONE; + + // Set the position data + desc.Triangles.VertexBuffer.StartAddress = pVb->getGpuAddress() + (mesh.vbOffset * pVbLayout->getStride()); + desc.Triangles.VertexBuffer.StrideInBytes = pVbLayout->getStride(); + desc.Triangles.VertexCount = mesh.vertexCount; + desc.Triangles.VertexFormat = getDxgiFormat(pVbLayout->getElementFormat(0)); + + // Set index data + desc.Triangles.IndexBuffer = pIb->getGpuAddress() + (mesh.ibOffset * getFormatBytesPerBlock(mpVao->getIndexBufferFormat())); + desc.Triangles.IndexCount = mesh.indexCount; + desc.Triangles.IndexFormat = getDxgiFormat(mpVao->getIndexBufferFormat()); + } + + mHasSkinnedMesh |= blas.hasSkinnedMesh; + } + } + + void Scene::buildBlas(RenderContext* pContext) + { + PROFILE("buildBlas"); + + // Get the VB and IB + const VertexBufferLayout::SharedConstPtr& pVbLayout = mpVao->getVertexLayout()->getBufferLayout(kStaticDataBufferIndex); + const Buffer::SharedPtr& pVb = mpVao->getVertexBuffer(kStaticDataBufferIndex); + const Buffer::SharedPtr& pIb = mpVao->getIndexBuffer(); + pContext->resourceBarrier(pVb.get(), Resource::State::NonPixelShader); + pContext->resourceBarrier(pIb.get(), Resource::State::NonPixelShader); + + // For each BLAS + for (uint32_t i = 0; i < (uint32_t)mBlasData.size(); i++) + { + auto& blas = mBlasData[i]; + auto& meshList = blas.meshList; + + if (blas.pBlas != nullptr && !blas.hasSkinnedMesh) continue; // Skip updating BLASes not containing skinned meshes + + // Setup build parameters and get prebuild info + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS inputs = {}; + inputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + inputs.NumDescs = (uint32_t)blas.geomDescs.size(); + inputs.pGeometryDescs = blas.geomDescs.data(); + inputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE; + + // Determine if this BLAS is, or will be refit, and add necessary flags + if (blas.hasSkinnedMesh && mBlasUpdateMode == UpdateMode::Refit) + { + inputs.Flags |= D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_ALLOW_UPDATE; // Subsequent updates need this flag too + + // Refit if BLAS exists, and it was previously created with ALLOW_UPDATE + if (blas.pBlas != nullptr && blas.updateMode == UpdateMode::Refit) inputs.Flags |= D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PERFORM_UPDATE; + } + + // Allocate scratch and BLAS buffers on the first build + if (blas.pBlas == nullptr) + { + GET_COM_INTERFACE(gpDevice->getApiHandle(), ID3D12Device5, pDevice5); + pDevice5->GetRaytracingAccelerationStructurePrebuildInfo(&inputs, &blas.prebuildInfo); + + // #SCENE This isn't guaranteed according to the spec, and the scratch buffer being stored should be sized differently depending on update mode + assert(blas.prebuildInfo.UpdateScratchDataSizeInBytes <= blas.prebuildInfo.ScratchDataSizeInBytes); + + blas.pScratchBuffer = Buffer::create(blas.prebuildInfo.ScratchDataSizeInBytes, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + blas.pBlas = Buffer::create(blas.prebuildInfo.ResultDataMaxSizeInBytes, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + } + // For any rebuild and refits, just add a barrier + else + { + assert(blas.pScratchBuffer != nullptr); + + pContext->uavBarrier(blas.pBlas.get()); + pContext->uavBarrier(blas.pScratchBuffer.get()); + } + + // Build BLAS + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC asDesc = {}; + asDesc.Inputs = inputs; + asDesc.ScratchAccelerationStructureData = blas.pScratchBuffer->getGpuAddress(); + asDesc.DestAccelerationStructureData = blas.pBlas->getGpuAddress(); + + // Set buffer address to update in place if this is a refit + if ((inputs.Flags & D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PERFORM_UPDATE) > 0) asDesc.SourceAccelerationStructureData = asDesc.DestAccelerationStructureData; + + GET_COM_INTERFACE(pContext->getLowLevelData()->getCommandList(), ID3D12GraphicsCommandList4, pList4); + pList4->BuildRaytracingAccelerationStructure(&asDesc, 0, nullptr); + + // Insert a UAV barrier + pContext->uavBarrier(blas.pBlas.get()); + + if (!blas.hasSkinnedMesh) blas.pScratchBuffer.reset(); // Release + else blas.updateMode = mBlasUpdateMode; + } + } + + void Scene::fillInstanceDesc(std::vector& instanceDescs, uint32_t rayCount, bool perMeshHitEntry) + { + instanceDescs.clear(); + uint32_t instanceContributionToHitGroupIndex = 0; + uint32_t instanceId = 0; + for (uint32_t i = 0; i < (uint32_t)mBlasData.size(); i++) + { + auto& meshList = mBlasData[i].meshList; + D3D12_RAYTRACING_INSTANCE_DESC desc = {}; + desc.AccelerationStructure = mBlasData[i].pBlas->getGpuAddress(); + desc.InstanceMask = 0xFF; + desc.InstanceContributionToHitGroupIndex = perMeshHitEntry ? instanceContributionToHitGroupIndex : 0; + instanceContributionToHitGroupIndex += rayCount * (uint32_t)meshList.size(); + + // If multiple meshes are in a BLAS: + // - Their global matrix is the same. + // - From sortBlasMeshes(), each mesh in the BLAS is guaranteed to be non-instanced, so only one INSTANCE_DESC is needed + if (meshList.size() > 1) + { + desc.InstanceID = instanceId; + instanceId += (uint32_t)meshList.size(); + + // Any instances of the mesh will get you the correct matrix, so just pick the first mesh then the first instance. + uint32_t firstInstanceId = mMeshIdToInstanceIds[meshList[0]][0]; + uint32_t matrixId = mMeshInstanceData[firstInstanceId].globalMatrixID; + mat4 transform4x4 = transpose(mpAnimationController->getGlobalMatrices()[matrixId]); + std::memcpy(desc.Transform, &transform4x4, sizeof(desc.Transform)); + instanceDescs.push_back(desc); + } + // If only one mesh is in the BLAS, there CAN be multiple instances of it. It is either: + // - A non-instanced mesh that was unable to be merged with others + // - A mesh with multiple instances + else + { + // For every instance of the mesh, create an INSTANCE_DESC + auto& instanceList = mMeshIdToInstanceIds[meshList[0]]; + for (uint32_t instId : instanceList) + { + desc.InstanceID = instanceId++; + uint32_t matrixId = mMeshInstanceData[instId].globalMatrixID; + mat4 transform4x4 = transpose(mpAnimationController->getGlobalMatrices()[matrixId]); + std::memcpy(desc.Transform, &transform4x4, sizeof(desc.Transform)); + instanceDescs.push_back(desc); + } + } + } + } + + void Scene::buildTlas(RenderContext* pContext, uint32_t rayCount, bool perMeshHitEntry) + { + PROFILE("buildTlas"); + + TlasData tlas; + auto it = mTlasCache.find(rayCount); + if (it != mTlasCache.end()) tlas = it->second; + + fillInstanceDesc(mInstanceDescs, rayCount, perMeshHitEntry); + + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS inputs = {}; + inputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + inputs.NumDescs = (uint32_t)mInstanceDescs.size(); + inputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE; + + // Add build flags for dynamic scenes if TLAS should be updating instead of rebuilt + if (mpAnimationController->hasAnimations() && mTlasUpdateMode == UpdateMode::Refit) + { + inputs.Flags |= D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_ALLOW_UPDATE; + + // If TLAS has been built already and it was built with ALLOW_UPDATE + if(tlas.pTlas != nullptr && tlas.updateMode == UpdateMode::Refit) inputs.Flags |= D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PERFORM_UPDATE; + } + + tlas.updateMode = mTlasUpdateMode; + + // On first build for the scene, create scratch buffer and cache prebuild info. As long as INSTANCE_DESC count doesn't change, we can reuse these + if (mpTlasScratch == nullptr) + { + // Prebuild + GET_COM_INTERFACE(gpDevice->getApiHandle(), ID3D12Device5, pDevice5); + pDevice5->GetRaytracingAccelerationStructurePrebuildInfo(&inputs, &mTlasPrebuildInfo); + mpTlasScratch = Buffer::create(mTlasPrebuildInfo.ScratchDataSizeInBytes, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + + // #SCENE This isn't guaranteed according to the spec, and the scratch buffer being stored should be sized differently depending on update mode + assert(mTlasPrebuildInfo.UpdateScratchDataSizeInBytes <= mTlasPrebuildInfo.ScratchDataSizeInBytes); + } + + // Setup GPU buffers + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC asDesc = {}; + asDesc.Inputs = inputs; + + // If first time building this TLAS + if (tlas.pTlas == nullptr) + { + assert(tlas.pInstanceDescs == nullptr); // Instance desc should also be null if no TLAS + tlas.pTlas = Buffer::create(mTlasPrebuildInfo.ResultDataMaxSizeInBytes, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + tlas.pInstanceDescs = Buffer::create((uint32_t)mInstanceDescs.size() * sizeof(D3D12_RAYTRACING_INSTANCE_DESC), Buffer::BindFlags::None, Buffer::CpuAccess::Write, mInstanceDescs.data()); + } + // Else update instance descs and barrier TLAS buffers + else + { + assert(mpAnimationController->hasAnimations()); + pContext->uavBarrier(tlas.pTlas.get()); + pContext->uavBarrier(mpTlasScratch.get()); + tlas.pInstanceDescs->setBlob(mInstanceDescs.data(), 0, inputs.NumDescs * sizeof(D3D12_RAYTRACING_INSTANCE_DESC)); + asDesc.SourceAccelerationStructureData = tlas.pTlas->getGpuAddress(); // Perform the update in-place + } + + assert((inputs.NumDescs != 0) && tlas.pInstanceDescs->getApiHandle() && tlas.pTlas->getApiHandle() && mpTlasScratch->getApiHandle()); + + asDesc.Inputs.InstanceDescs = tlas.pInstanceDescs->getGpuAddress(); + asDesc.ScratchAccelerationStructureData = mpTlasScratch->getGpuAddress(); + asDesc.DestAccelerationStructureData = tlas.pTlas->getGpuAddress(); + + // Set the source buffer to update in place if this is an update + if ((inputs.Flags & D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PERFORM_UPDATE) > 0) asDesc.SourceAccelerationStructureData = asDesc.DestAccelerationStructureData; + + // Create TLAS + GET_COM_INTERFACE(pContext->getLowLevelData()->getCommandList(), ID3D12GraphicsCommandList4, pList4); + pContext->resourceBarrier(tlas.pInstanceDescs.get(), Resource::State::NonPixelShader); + pList4->BuildRaytracingAccelerationStructure(&asDesc, 0, nullptr); + pContext->uavBarrier(tlas.pTlas.get()); + + // Create TLAS SRV + if (tlas.pSrv == nullptr) + { + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.RaytracingAccelerationStructure.Location = tlas.pTlas->getGpuAddress(); + + DescriptorSet::Layout layout; + layout.addRange(DescriptorSet::Type::TextureSrv, 0, 1); + DescriptorSet::SharedPtr pSet = DescriptorSet::create(gpDevice->getCpuDescriptorPool(), layout); + assert(pSet); + gpDevice->getApiHandle()->CreateShaderResourceView(nullptr, &srvDesc, pSet->getCpuHandle(0)); + + ResourceWeakPtr pWeak = tlas.pTlas; + tlas.pSrv = std::make_shared(pWeak, pSet, 0, 1, 0, 1); + } + + mTlasCache[rayCount] = tlas; + } + + void Scene::updateAsToInstanceDataMapping() + { + // Calculate acceleration structure indexing to mMeshInstanceData index + // Essentially: mMeshInstanceData[ buffer[TLAS InstanceID() + GeometryIndex] ] + // Here, just append mesh instance ID's in order they'd appear in the TLAS. + std::vector asToInstanceMapping; + for (uint32_t blasIndex = 0; blasIndex < (uint32_t)mBlasData.size(); blasIndex++) + { + for (const uint32_t& meshId : mBlasData[blasIndex].meshList) + { + auto& instList = mMeshIdToInstanceIds[meshId]; + for (const uint32_t instId : instList) + { + asToInstanceMapping.push_back(instId); + } + } + } + + mpAsToInstanceMapping = Buffer::create(asToInstanceMapping.size() * sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, asToInstanceMapping.data()); + } + + void Scene::setGeometryIndexIntoRtVars(const std::shared_ptr& pRtVars) + { + // Set BLAS geometry index as constant buffer data + for (uint32_t ray = 0; ray < pRtVars->getHitProgramsCount(); ray++) + { + RtProgramVars::VarsVector& rayVars = pRtVars->getHitVars(ray); + + uint32_t blasIndex = 0; + uint32_t geometryIndex = 0; + for (uint32_t i = 0; i < rayVars.size(); i++) + { + auto& pVar = rayVars[i]; + pVar["DxrPerGeometry"]["geometryIndex"] = geometryIndex++; + + // If at the end of this BLAS, reset counters and start checking next BLAS + uint32_t geomCount = (uint32_t)mBlasData[blasIndex].meshList.size(); + if (geometryIndex == geomCount) + { + geometryIndex = 0; + blasIndex++; + } + } + } + } + + void Scene::setEnvironmentMap(Texture::ConstSharedPtrRef pEnvMap) + { + if (mpEnvMap == pEnvMap) return; + mpEnvMap = pEnvMap; + mpSceneBlock["envMap"] = mpEnvMap; + } + + void Scene::setCameraAspectRatio(float ratio) + { + mCamera.pObject->setAspectRatio(ratio); + } + + void Scene::bindSamplerToMaterials(Sampler::ConstSharedPtrRef pSampler) + { + for (auto& pMaterial : mMaterials) + { + pMaterial->setSampler(pSampler); + } + } + + void Scene::setCameraController(CameraControllerType type) + { + if (mCamCtrlType == type && mpCamCtrl) return; + + switch (type) + { + case CameraControllerType::FirstPerson: + mpCamCtrl = FirstPersonCameraController::create(mCamera.pObject); + break; + case CameraControllerType::Orbiter: + mpCamCtrl = OrbiterCameraController::create(mCamera.pObject); + ((OrbiterCameraController*)mpCamCtrl.get())->setModelParams(mSceneBB.center, length(mSceneBB.extent), 3.5f); + break; + case CameraControllerType::SixDOF: + mpCamCtrl = SixDoFCameraController::create(mCamera.pObject); + break; + default: + should_not_get_here(); + } + mpCamCtrl->setCameraSpeed(mCameraSpeed); + } + + bool Scene::onMouseEvent(const MouseEvent& mouseEvent) + { + return mpCamCtrl->onMouseEvent(mouseEvent); + } + + bool Scene::onKeyEvent(const KeyboardEvent& keyEvent) + { + return mpCamCtrl->onKeyEvent(keyEvent); + } +} diff --git a/Source/Falcor/Scene/Scene.h b/Source/Falcor/Scene/Scene.h new file mode 100644 index 000000000..b4d7a75b0 --- /dev/null +++ b/Source/Falcor/Scene/Scene.h @@ -0,0 +1,458 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/API/VAO.h" +#include "Data/HostDeviceData.h" +#include "Animation/Animation.h" +#include "Lights/Light.h" +#include "Lights/LightProbe.h" +#include "Camera/Camera.h" +#include "Material/Material.h" +#include "Utils/Math/AABB.h" +#include "Animation/AnimationController.h" +#include "Camera/CameraController.h" + +namespace Falcor +{ + /** DXR Scene and Resources Layout: + - BLAS creation logic is similar to Falcor 3.0, and are grouped in the following order: + 1) For non-instanced meshes, group them if they use the same scene graph transform matrix. One BLAS is created per group. + a) It is possible a non-instanced mesh has no other meshes to merge with. In that case, the mesh goes in its own BLAS. + 2) For instanced meshes, one BLAS is created per mesh. + + - TLAS Construction: + - Hit shaders use InstanceID() and GeometryIndex() to identify what was hit. + - InstanceID is set like a starting offset so that (InstanceID + GeometryIndex) maps to unique indices. + - Shader table has one hit group per mesh. InstanceContribution is set accordingly for correct lookup. + + Acceleration Structure Layout Example (Scene with 8 meshes, 10 instances total): + + ---------------------------------------------------------------- + | Value(s) | + --------------------------------------------------------------------------------------- + | InstanceID | 0 | 4 | 5 | 6 | 7 | 8 | 9 | 7 INSTANCE_DESCs in TLAS + | InstanceContribution | 0 | 4 | 5 | 6 | 7 | 7 | 7 | Helps look up one hit group per MESH + | BLAS Geometry Index | 0 , 1 , 2 , 3 | 0 | 0 | 0 | 0 | 5 BLAS's containing 8 meshes total + --------------------------------------------------------------------------------------- + | Notes | Meshes merged into | One instance | Multiple instances | + | | one BLAS | per mesh | of a mesh | + --------------------------------------------------------------------------------------| + + - updateAsToInstanceDataMapping() creates a lookup table that translates "InstanceID() + GeometryIndex()" to the Global Hit ID, which is an index into MeshInstanceData. + - Mesh instance ID = LookupTable[InstanceID() + GeometryIndex()] + - This is wrapped in getGlobalHitID() in Raytracing.slang. + */ + + class dlldecl Scene + { + public: + using SharedPtr = std::shared_ptr; + using ConstSharedPtrRef = const SharedPtr&; + using LightList = std::vector; + static const uint32_t kMaxBonesPerVertex = 4; + static const FileDialogFilterVec kFileExtensionFilters; + + static SharedPtr create(const std::string& filename); + + // #SCENE: we should get rid of this. We can't right now because we can't create a structured-buffer of materials (MaterialData contains textures) + Shader::DefineList getSceneDefines(); + + enum class RenderFlags + { + None = 0x0, + UserRasterizerState = 0x1, ///< Use the rasterizer state currently bound to `pState`. If this flag is not set, the default rasterizer state will be used. + ///< Note that we need to change the rasterizer state during rendering because some meshes have a negative scale factor, and hence the triangles will have a different winding order. + ///< If such meshes exist, overriding the state may result in incorrect rendering output + }; + + /** Flags indicating if and what was updated in the scene + */ + enum class UpdateFlags + { + None = 0x0, ///< Nothing happened + MeshesMoved = 0x1, ///< Meshes moved + CameraMoved = 0x2, ///< The camera moved + CameraPropertiesChanged = 0x4, ///< Some camera properties changed, excluding position + LightsMoved = 0x8, ///< Lights were moved + LightIntensityChanged = 0x10, ///< Light intensity changed + LightPropertiesChanged = 0x20, ///< Other light changes not included in LightIntensityChanged and LightsMoved + SceneGraphChanged = 0x40, ///< Any transform in the scene graph changed. + + All = -1 + }; + + /** Settings for how the scene is updated + */ + enum class UpdateMode + { + Rebuild, ///< Recreate acceleration structure when updates are needed + Refit ///< Update acceleration structure when updates are needed + }; + + enum class CameraControllerType + { + FirstPerson, + Orbiter, + SixDOF + }; + + /** Access the scene's camera to change properties, or use elsewhere. + */ + const Camera::SharedPtr& getCamera() { return mCamera.pObject; } + + /** Attach a new camera to the scene + */ + void setCamera(const Camera::SharedPtr& pCamera) { mCamera.pObject = pCamera; } + + /** Set the camera's aspect ratio + */ + void setCameraAspectRatio(float ratio); + + /** Set a camera controller type + */ + void setCameraController(CameraControllerType type); + + /** Get the camera controller type + */ + CameraControllerType getCameraControllerType() const { return mCamCtrlType; } + + /** Reset the camera. + This function will place the camera at the center of scene and optionally set the depth range to some reasonable pre-determined values + */ + void resetCamera(bool resetDepthRange = true); + + /** Set the camera's speed + */ + void setCameraSpeed(float speed) { mCameraSpeed = speed; } + + /** Get the camera's speed + */ + float getCameraSpeed() const { return mCameraSpeed; } + + /** Get the number of meshes + */ + uint32_t getMeshCount() const { return (uint32_t)mMeshDesc.size(); } + + /** Get a mesh desc + */ + const MeshDesc& getMesh(uint32_t meshID) const { return mMeshDesc[meshID]; } + + /** Get the number of mesh instances + */ + uint32_t getMeshInstanceCount() const { return (uint32_t)mMeshInstanceData.size(); } + + /** Get a mesh instance desc + */ + const MeshInstanceData& getMeshInstance(uint32_t instanceID) const { return mMeshInstanceData[instanceID]; } + + /** Get the number of materials in the scene + */ + uint32_t getMaterialCount() const { return (uint32_t)mMaterials.size(); } + + /** Get a material + */ + Material::ConstSharedPtrRef getMaterial(uint32_t materialID) const { return mMaterials[materialID]; } + + /** Get the scene bounds + */ + const BoundingBox& getSceneBounds() const { return mSceneBB; } + + /** Get a mesh's bounds + */ + const BoundingBox& getMeshBounds(uint32_t meshID) const { return mMeshBBs[meshID]; } + + /** Get the number of lights in the scene + */ + uint32_t getLightCount() const { return (uint32_t)mLights.size(); } + + /** Get a light + */ + Light::ConstSharedPtrRef getLight(uint32_t lightID) const { return mLights[lightID].pObject; } + + /** Get the light probe or nullptr if it doesn't exist. + */ + const LightProbe::SharedPtr& getLightProbe() const { return mpLightProbe; } + + /** Get/Set how the scene's TLASes are updated when raytracing. + TLASes are REBUILT by default + */ + void setTlasUpdateMode(UpdateMode mode) { mTlasUpdateMode = mode; } + UpdateMode getTlasUpdateMode() { return mTlasUpdateMode; } + + /** Get/Set how the scene's BLASes are updated when raytracing. + BLASes are REFIT by default + */ + void setBlasUpdateMode(UpdateMode mode) { mBlasUpdateMode = mode; } + UpdateMode getBlasUpdateMode() { return mBlasUpdateMode; } + + /** Update the scene. Call this once per frame to update the camera location, animations, etc. + \param pContext + \param currentTime The current time in seconds + */ + UpdateFlags update(RenderContext* pContext, double currentTime); + + /** Get the changes that happened during the last update + The flags only change during an `update()` call, if something changed between calling `update()` and `getUpdates()`, the returned result will not reflect it + */ + UpdateFlags getUpdates() const { return mUpdates; } + + /** Render the scene using the rasterizer + */ + void render(RenderContext* pContext, GraphicsState* pState, GraphicsVars* pVars, RenderFlags flags = RenderFlags::None); + + /** Render the scene using raytracing + */ + void raytrace(RenderContext* pContext, const std::shared_ptr& pState, const std::shared_ptr& pVars, uvec3 dispatchDims); + + /** Render the UI + */ + void renderUI(Gui::Widgets& widget); + + /** Bind a sampler to the materials + */ + void bindSamplerToMaterials(Sampler::ConstSharedPtrRef pSampler); + + /** Get the scene's VAO + */ + const Vao::SharedPtr& getVao() const { return mpVao; } + + /** Set an environment map + */ + void setEnvironmentMap(Texture::ConstSharedPtrRef pEnvMap); + + /** Get the environment map + */ + Texture::ConstSharedPtrRef getEnvironmentMap() const { return mpEnvMap; } + + /** Handle mouse events + */ + bool onMouseEvent(const MouseEvent& mouseEvent); + + /** Handle keyboard events + */ + bool onKeyEvent(const KeyboardEvent& keyEvent); + + /** Get the filename that the scene was loaded from + */ + const std::string& getFilename() const { return mFilename; } + + /** Get the animation controller. + */ + const AnimationController* getAnimationController() const { return mpAnimationController.get(); } + + /** Get the parameter block with all scene resources. + Note that the camera is not bound automatically. + */ + ParameterBlock::ConstSharedPtrRef getParameterBlock() const { return mpSceneBlock; } + + private: + friend class SceneBuilder; + friend class AnimationController; + + static constexpr uint32_t kStaticDataBufferIndex = 0; + static constexpr uint32_t kDrawIdBufferIndex = kStaticDataBufferIndex + 1; + static constexpr uint32_t kVertexBufferCount = kDrawIdBufferIndex + 1; + + static SharedPtr create(); + + /** Create scene parameter block and retrieve pointers to buffers + */ + void initResources(); + + /** Uploads scene data to parameter block + */ + void uploadResources(); + + /** Verify variable offsets in GPU buffers are consistent with CPU data. + */ + void checkOffsets(); + + /** Update the scene's global bounding box. + */ + void updateBounds(); + + /** Update mesh instance flags + */ + void updateMeshInstanceFlags(); + + /** Do any additional initialization required after scene data is set and draw lists are determined. + */ + void finalize(); + + /** Create the draw list for rasterization + */ + void createDrawList(); + + /** Sort what meshes go in what BLAS. Results stored in mBlasBuckets. + */ + void sortBlasMeshes(); + + /** Initialize geometry descs for each BLAS + */ + void initGeomDesc(); + + /** Generate bottom level acceleration structures for all meshes + */ + void buildBlas(RenderContext* pContext); + + /** Generate data for creating a TLAS. + #SCENE TODO: Add argument to build descs based off a draw list + */ + void fillInstanceDesc(std::vector& instanceDescs, uint32_t rayCount, bool perMeshHitEntry); + + /** Generate top level acceleration structure for the scene. Automatically determines whether to build or refit. + \param[in] rayCount Number of ray types in the shader. Required to setup how instances index into the Shader Table + */ + void buildTlas(RenderContext* pContext, uint32_t rayCount, bool perMeshHitEntry); + + /** Set the BLAS geometry index into their local constant buffer. + + This is a workaround before GeometryIndex() is supported in shaders. + */ + void setGeometryIndexIntoRtVars(const std::shared_ptr& pVars); + + /** Create the buffer that maps Acceleration Structure indices to their location in mMeshInstanceData + mMeshInstanceData should be indexed with [InstanceID() + GeometryIndex] + */ + void updateAsToInstanceDataMapping(); + + UpdateFlags updateCamera(bool forceUpdate); + UpdateFlags updateLights(bool forceUpdate); + + template + struct AnimatedObject + { + typename Object::SharedPtr pObject; + bool animate = true; + size_t nodeID = kInvalidNode; + bool update(const AnimationController* pAnimCtrl, bool force); + bool hasGlobalTransform() const { return nodeID != kInvalidNode; } + void setIntoObject(const vec3& pos, const vec3& up, const vec3& lookAt); + bool enabled(bool force) const; + }; + + Scene(); + + // Scene Geometry + Vao::SharedPtr mpVao; + struct DrawArgs + { + Buffer::SharedPtr pBuffer; + uint32_t count = 0; + } mDrawClockwiseMeshes, mDrawCounterClockwiseMeshes, mDrawAlphaTestedMeshes; + + static const uint32_t kInvalidNode = -1; + + struct Node + { + Node() = default; + Node(const std::string& n, uint32_t p, const mat4& t, const mat4& l2b) : parent(p), name(n), transform(t), localToBindSpace(l2b) {}; + std::string name; + uint32_t parent = kInvalidNode; + mat4 transform; // The node's transformation matrix + mat4 localToBindSpace; // Local to bind space transformation + }; + + // #SCENE We don't need those vectors on the host + std::vector mMeshDesc; ///< Copy of GPU buffer (mpMeshes) + std::vector mMeshInstanceData; ///< Copy of GPU buffer (mpMeshInstances) + std::vector mSceneGraph; ///< For each index i, the array element indicates the parent node. Indices are in relation to mLocalToWorldMatrices + + std::vector mMaterials; ///< Bound to parameter block + std::vector> mLights; ///< Bound to parameter block + LightProbe::SharedPtr mpLightProbe; ///< Bound to parameter block + Texture::SharedPtr mpEnvMap; ///< Not bound to anything, not rendered automatically. Can be used to render a skybox + + // Scene Metadata (CPU Only) + std::vector mMeshBBs; ///< Bounding boxes for meshes (not instances) + std::vector> mMeshIdToInstanceIds; ///< Mapping of what instances belong to which mesh + BoundingBox mSceneBB; ///< Bounding boxes of the entire scene + std::vector mMeshHasDynamicData; ///< Whether a Mesh has dynamic data, meaning it is skinned + + // Resources + StructuredBuffer::SharedPtr mpMeshesBuffer; + StructuredBuffer::SharedPtr mpMeshInstancesBuffer; + StructuredBuffer::SharedPtr mpLightsBuffer; + ParameterBlock::SharedPtr mpSceneBlock; + + // Camera + CameraControllerType mCamCtrlType = CameraControllerType::FirstPerson; + CameraController::SharedPtr mpCamCtrl; + AnimatedObject mCamera; + float mCameraSpeed = 1.0f; + + // Rendering + RasterizerState::SharedPtr mpFrontClockwiseRS; + RasterizerState::SharedPtr mpNoCullRS; + UpdateFlags mUpdates = UpdateFlags::All; + AnimationController::UniquePtr mpAnimationController; + + // Raytracing Data + UpdateMode mTlasUpdateMode = UpdateMode::Rebuild; ///< How the TLAS should be updated when there are changes in the scene + UpdateMode mBlasUpdateMode = UpdateMode::Refit; ///< How the BLAS should be updated when there are changes to meshes + + std::vector mInstanceDescs; ///< Shared between TLAS builds to avoid reallocating CPU memory + + struct TlasData + { + Buffer::SharedPtr pTlas; + ShaderResourceView::SharedPtr pSrv; ///< Shader Resource View for binding the TLAS + Buffer::SharedPtr pInstanceDescs; ///< Buffer holding instance descs for the TLAS + UpdateMode updateMode = UpdateMode::Rebuild; ///< Update mode this TLAS was created with. + }; + + std::unordered_map mTlasCache; ///< Top Level Acceleration Structure for scene data cached per shader ray count + ///< Number of ray types in program affects Shader Table indexing + Buffer::SharedPtr mpTlasScratch; ///< Scratch buffer used for TLAS builds. Can be shared as long as instance desc count is the same, which for now it is. + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO mTlasPrebuildInfo; ///< This can be reused as long as the number of instance descs doesn't change. + + struct BlasData + { + BlasData(const std::vector& meshList) : meshList(meshList) {} + + Buffer::SharedPtr pBlas; + Buffer::SharedPtr pScratchBuffer; + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO prebuildInfo; + std::vector geomDescs; + std::vector meshList; ///< List of meshId's that are part of each BLAS + bool hasSkinnedMesh = false; ///< Whether the BLAS contains a skinned mesh, which means the BLAS may need to be updated + UpdateMode updateMode = UpdateMode::Refit; ///< Update mode this BLAS was created with. + }; + + std::vector mBlasData; ///< All data related to the scene's BLASes + bool mHasSkinnedMesh = false; ///< Whether the scene has a skinned mesh at all. + + Buffer::SharedPtr mpAsToInstanceMapping; ///< Lookup table from [InstanceID() + GeometryIndex()] to mMeshInstanceData index + std::unordered_set mRtVarsWithGeometryIndex; ///< Lookup for what RtProgramVars have had geometryIndex set for each mesh. Cleared/invalidated when BLAS changes. + std::string mFilename; + }; + + enum_class_operators(Scene::RenderFlags); + enum_class_operators(Scene::UpdateFlags); +} diff --git a/Source/Falcor/Scene/SceneBuilder.cpp b/Source/Falcor/Scene/SceneBuilder.cpp new file mode 100644 index 000000000..bcf682566 --- /dev/null +++ b/Source/Falcor/Scene/SceneBuilder.cpp @@ -0,0 +1,451 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "SceneBuilder.h" +#include "../Externals/mikktspace/mikktspace.h" +#include + +namespace Falcor +{ + namespace + { + class MikkTSpaceWrapper + { + public: + static std::vector generateBitangents(const vec3* pPositions, const vec3* pNormals, const vec2* pTexCrd, const uint32_t* pIndices, size_t vertexCount, size_t indexCount) + { + if (!pNormals || !pPositions || !pTexCrd || !pIndices) + { + logWarning("Can't generate tangent space. The mesh doesn't have positions/normals/texCrd/indices"); + return std::vector(vertexCount, vec3(0, 0, 0)); + } + + SMikkTSpaceInterface mikktspace = {}; + mikktspace.m_getNumFaces = [](const SMikkTSpaceContext* pContext) {return ((MikkTSpaceWrapper*)(pContext->m_pUserData))->getFaceCount(); }; + mikktspace.m_getNumVerticesOfFace = [](const SMikkTSpaceContext * pContext, int32_t face) {return 3; }; + mikktspace.m_getPosition = [](const SMikkTSpaceContext * pContext, float position[], int32_t face, int32_t vert) {((MikkTSpaceWrapper*)(pContext->m_pUserData))->getPosition(position, face, vert); }; + mikktspace.m_getNormal = [](const SMikkTSpaceContext * pContext, float normal[], int32_t face, int32_t vert) {((MikkTSpaceWrapper*)(pContext->m_pUserData))->getNormal(normal, face, vert); }; + mikktspace.m_getTexCoord = [](const SMikkTSpaceContext * pContext, float texCrd[], int32_t face, int32_t vert) {((MikkTSpaceWrapper*)(pContext->m_pUserData))->getTexCrd(texCrd, face, vert); }; + mikktspace.m_setTSpaceBasic = [](const SMikkTSpaceContext * pContext, const float tangent[], float sign, int32_t face, int32_t vert) {((MikkTSpaceWrapper*)(pContext->m_pUserData))->setTangent(tangent, sign, face, vert); }; + + MikkTSpaceWrapper wrapper(pPositions, pNormals, pTexCrd, pIndices, vertexCount, indexCount); + SMikkTSpaceContext context = {}; + context.m_pInterface = &mikktspace; + context.m_pUserData = &wrapper; + + if (genTangSpaceDefault(&context) == false) + { + logError("Failed to generate MikkTSpace tangents"); + return std::vector(vertexCount, vec3(0, 0, 0)); + } + + return wrapper.mBitangents; + } + + private: + MikkTSpaceWrapper(const vec3* pPositions, const vec3* pNormals, const vec2* pTexCrd, const uint32_t* pIndices, size_t vertexCount, size_t indexCount) : + mpPositions(pPositions), mpNormals(pNormals), mpTexCrd(pTexCrd), mpIndices(pIndices), mFaceCount(indexCount / 3), mBitangents(vertexCount) {} + const vec3* mpPositions; + const vec3* mpNormals; + const vec2* mpTexCrd; + const uint32_t* mpIndices; + size_t mFaceCount; + std::vector mBitangents; + int32_t getFaceCount() const { return (int32_t)mFaceCount; } + int32_t getIndex(int32_t face, int32_t vert) { return mpIndices[face * 3 + vert]; } + void getPosition(float position[], int32_t face, int32_t vert) { *(vec3*)position = mpPositions[getIndex(face, vert)]; } + void getNormal(float normal[], int32_t face, int32_t vert) { *(vec3*)normal = mpNormals[getIndex(face, vert)]; } + void getTexCrd(float texCrd[], int32_t face, int32_t vert) { *(vec2*)texCrd = mpTexCrd[getIndex(face, vert)]; } + + void setTangent(const float tangent[], float sign, int32_t face, int32_t vert) + { + int32_t index = getIndex(face, vert); + vec3 T(*(vec3*)tangent), N; + getNormal(&N[0], face, vert); + // bitangent = fSign * cross(vN, tangent); + mBitangents[index] = cross(N, T); // Not using fSign because... I don't know why. It flips the tangent space. Need to go read the paper + } + }; + + void validateTangentSpace(const vec3 bitangents[], uint32_t vertexCount) + { + auto isValid = [](const vec3& bitangent) + { + if (glm::any(glm::isinf(bitangent) || glm::isnan(bitangent))) return false; + if (length(bitangent) < 1e-6f) return false; + return true; + }; + + uint32_t numInvalid = 0; + for (uint32_t i = 0; i < vertexCount; i++) + { + if (!isValid(bitangents[i])) numInvalid++; + } + + if (numInvalid > 0) + { + logWarning("Loaded tangent space is invalid at " + std::to_string(numInvalid) + " vertices. Please fix the asset."); + } + } + } + + SceneBuilder::SceneBuilder(Flags flags) : mFlags(flags) {}; + + SceneBuilder::SharedPtr SceneBuilder::create(Flags flags) + { + return SharedPtr(new SceneBuilder(flags)); + } + + SceneBuilder::SharedPtr SceneBuilder::create(const std::string& filename, Flags buildFlags, const InstanceMatrices& instances) + { + auto pBuilder = create(buildFlags); + return pBuilder->import(filename, instances) ? pBuilder : nullptr; + } + + bool SceneBuilder::import(const std::string& filename, const InstanceMatrices& instances) + { + if (std::filesystem::path(filename).extension() == ".fscene") + { + mFilename = filename; + return SceneImporter::import(filename, *this); + } + else + { + return AssimpImporter::import(filename, *this, instances); + } + } + + size_t SceneBuilder::addNode(const Node& node) + { + assert(node.parent == kInvalidNode || node.parent < mSceneGraph.size()); + + size_t newNodeID = mSceneGraph.size(); + assert(newNodeID <= UINT32_MAX); + mSceneGraph.push_back(InternalNode(node)); + if(node.parent != kInvalidNode) mSceneGraph[node.parent].children.push_back(newNodeID); + return newNodeID; + } + + void SceneBuilder::addMeshInstance(size_t nodeID, size_t meshID) + { + assert(meshID < mMeshes.size()); + mSceneGraph.at(nodeID).meshes.push_back(meshID); + mMeshes.at(meshID).instances.push_back((uint32_t)nodeID); + } + + size_t SceneBuilder::addMesh(const Mesh& mesh) + { + assert(mesh.pLightMapUVs == nullptr); + const auto& prevMesh = mMeshes.size() ? mMeshes.back() : MeshSpec(); + + // Create the new mesh spec + mMeshes.push_back({}); + MeshSpec& spec = mMeshes.back(); + assert(mBuffersData.staticData.size() <= UINT32_MAX && mBuffersData.dynamicData.size() <= UINT32_MAX && mBuffersData.indices.size() <= UINT32_MAX); + spec.staticVertexOffset = (uint32_t)mBuffersData.staticData.size(); + spec.dynamicVertexOffset = (uint32_t)mBuffersData.dynamicData.size(); + spec.indexOffset = (uint32_t)mBuffersData.indices.size(); + spec.indexCount = mesh.indexCount; + spec.vertexCount = mesh.vertexCount; + spec.topology = mesh.topology; + spec.materialId = addMaterial(mesh.pMaterial, !is_set(mFlags, Flags::RemoveDuplicateMaterials)); + + // Error checking + auto throw_on_missing_element = [&](const std::string& element) + { + throw std::runtime_error("Error when adding the mesh " + mesh.name + " to the scene.\nThe mesh is missing " + element); + }; + + auto missing_element_warning = [&](const std::string& element) + { + logWarning("The mesh " + mesh.name + " is missing the element " + element + ". This is not an error, the element will be filled with zeros which may result in incorrect rendering"); + }; + + // Initialize the static data + if (mesh.indexCount == 0 || !mesh.pIndices) throw_on_missing_element("indices"); + mBuffersData.indices.insert(mBuffersData.indices.end(), mesh.pIndices, mesh.pIndices + mesh.indexCount); + + if (mesh.vertexCount == 0) throw_on_missing_element("vertices"); + if (mesh.pPositions == nullptr) throw_on_missing_element("positions"); + if (mesh.pNormals == nullptr) missing_element_warning("normals"); + if (mesh.pTexCrd == nullptr) missing_element_warning("texture coordinates"); + + // Initialize the dynamic data + if (mesh.pBoneWeights || mesh.pBoneIDs) + { + if (mesh.pBoneIDs == nullptr) throw_on_missing_element("bone IDs"); + if (mesh.pBoneWeights == nullptr) throw_on_missing_element("bone weights"); + spec.hasDynamicData = true; + } + + // Generate tangent space if that's required + std::vector bitangents; + if (!is_set(mFlags, Flags::UseOriginalTangentSpace) || !mesh.pBitangents) + { + bitangents = MikkTSpaceWrapper::generateBitangents(mesh.pPositions, mesh.pNormals, mesh.pTexCrd, mesh.pIndices, mesh.vertexCount, mesh.indexCount); + } + else + { + validateTangentSpace(mesh.pBitangents, mesh.vertexCount); + } + + for (uint32_t v = 0; v < mesh.vertexCount; v++) + { + StaticVertexData s; + s.position = mesh.pPositions[v]; + s.normal = mesh.pNormals ? mesh.pNormals[v] : vec3(0, 0, 0); + s.texCrd = mesh.pTexCrd ? mesh.pTexCrd[v] : vec2(0, 0); + s.bitangent = bitangents.size() ? bitangents[v] : mesh.pBitangents[v]; + s.prevPosition = s.position; + mBuffersData.staticData.push_back(s); + + if (mesh.pBoneWeights) + { + DynamicVertexData d; + d.boneWeight = mesh.pBoneWeights[v]; + d.boneID = mesh.pBoneIDs[v]; + d.staticIndex = (uint32_t)mBuffersData.staticData.size() - 1; + mBuffersData.dynamicData.push_back(d); + } + +// if (mesh.pLightMapUVs) +// { +// spec.optionalData[v].lightmapUV = mesh.pLightMapUVs[v]; +// } + } + + return mMeshes.size() - 1; + } + + uint32_t SceneBuilder::addMaterial(const Material::SharedPtr& pMaterial, bool forceNew) + { + assert(pMaterial); + + if (!forceNew) + { + // Check if the material already exists + for (uint32_t i = 0; i < mMaterials.size(); i++) + { + if (*mMaterials[i] == *pMaterial) return i; + } + } + + mMaterials.push_back(pMaterial); + assert(mMaterials.size() <= UINT32_MAX); + return (uint32_t)mMaterials.size() - 1; + } + + void SceneBuilder::setCamera(const Camera::SharedPtr& pCamera, size_t nodeID) + { + mCamera.nodeID = nodeID; + mCamera.pObject = pCamera; + } + + size_t SceneBuilder::addLight(const Light::SharedPtr& pLight, size_t nodeID) + { + Scene::AnimatedObject light; + light.pObject = pLight; + light.nodeID = nodeID; + mLights.push_back(light); + return mLights.size() - 1; + } + + Vao::SharedPtr SceneBuilder::createVao(uint16_t drawCount) + { + for (auto& mesh : mMeshes) assert(mesh.topology == mMeshes[0].topology); + size_t ibSize = sizeof(uint32_t) * mBuffersData.indices.size(); + size_t staticVbSize = sizeof(StaticVertexData) * mBuffersData.staticData.size(); + assert(ibSize <= UINT32_MAX && staticVbSize <= UINT32_MAX); + ResourceBindFlags ibBindFlags = Resource::BindFlags::Index | ResourceBindFlags::ShaderResource; + Buffer::SharedPtr pIB = Buffer::create((uint32_t)ibSize, ibBindFlags, Buffer::CpuAccess::None, mBuffersData.indices.data()); + + // Create the static vertex data as a structured-buffer + ComputeProgram::SharedPtr pSkinning = ComputeProgram::createFromFile("Skinning.slang", "main"); + ReflectionVar::SharedConstPtr pReflector = pSkinning->getReflector()->getParameterBlock("gData")->getResource("skinnedVertices"); + ResourceBindFlags vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::Vertex; + StructuredBuffer::SharedPtr pStaticBuffer = StructuredBuffer::create(pReflector->getName(), std::dynamic_pointer_cast(pReflector->getType()), (uint32_t)mBuffersData.staticData.size(), vbBindFlags); + + Vao::BufferVec pVBs(Scene::kVertexBufferCount); + pVBs[Scene::kStaticDataBufferIndex] = pStaticBuffer; + std::vector drawIDs(drawCount); + for (uint32_t i = 0; i < drawCount; i++) drawIDs[i] = i; + pVBs[Scene::kDrawIdBufferIndex] = Buffer::create(drawCount*sizeof(uint16_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); + + // The layout only initialized the static and optional data. The skinning data doesn't get passed into the vertex-shader + VertexLayout::SharedPtr pLayout = VertexLayout::create(); + + // Static data + VertexBufferLayout::SharedPtr pStaticLayout = VertexBufferLayout::create(); + pStaticLayout->addElement(VERTEX_POSITION_NAME, offsetof(StaticVertexData, position), ResourceFormat::RGB32Float, 1, VERTEX_POSITION_LOC); + pStaticLayout->addElement(VERTEX_NORMAL_NAME, offsetof(StaticVertexData, normal), ResourceFormat::RGB32Float, 1, VERTEX_NORMAL_LOC); + pStaticLayout->addElement(VERTEX_BITANGENT_NAME, offsetof(StaticVertexData, bitangent), ResourceFormat::RGB32Float, 1, VERTEX_BITANGENT_LOC); + pStaticLayout->addElement(VERTEX_TEXCOORD_NAME, offsetof(StaticVertexData, texCrd), ResourceFormat::RG32Float, 1, VERTEX_TEXCOORD_LOC); + pStaticLayout->addElement(VERTEX_PREV_POSITION_NAME, offsetof(StaticVertexData, prevPosition), ResourceFormat::RGB32Float, 1, VERTEX_PREV_POSITION_LOC); + pLayout->addBufferLayout(Scene::kStaticDataBufferIndex, pStaticLayout); + + // Add the draw ID layout + VertexBufferLayout::SharedPtr pInstLayout = VertexBufferLayout::create(); + pInstLayout->addElement(INSTANCE_DRAW_ID_NAME, 0, ResourceFormat::R16Uint, 1, INSTANCE_DRAW_ID_LOC); + pInstLayout->setInputClass(VertexBufferLayout::InputClass::PerInstanceData, 1); + pLayout->addBufferLayout(Scene::kDrawIdBufferIndex, pInstLayout); + +// // #SCENE optional data +// if (pVBs[sOptionalDataIndex]) +// { +// VertexBufferLayout::SharedPtr pOptionalLayout = VertexBufferLayout::create(); +// pOptionalLayout->addElement(VERTEX_LIGHTMAP_UV_NAME, offsetof(SceneBuilder::MeshSpec::OptionalData, lightmapUV), ResourceFormat::RGB32Float, 1, VERTEX_LIGHTMAP_UV_LOC); +// pLayout->addBufferLayout(sOptionalDataIndex, pOptionalLayout); +// } + + Vao::SharedPtr pVao = Vao::create(mMeshes[0].topology, pLayout, pVBs, pIB, ResourceFormat::R32Uint); + return pVao; + } + + void SceneBuilder::createGlobalMatricesBuffer(Scene* pScene) + { + pScene->mSceneGraph.resize(mSceneGraph.size()); + + for (uint32_t i = 0; i < mSceneGraph.size(); i++) + { + assert(mSceneGraph[i].parent <= UINT32_MAX); + pScene->mSceneGraph[i] = Scene::Node( mSceneGraph[i].name, (uint32_t)mSceneGraph[i].parent, mSceneGraph[i].transform, mSceneGraph[i].localToBindPose); + } + } + + uint32_t SceneBuilder::createMeshData(Scene* pScene) + { + auto& meshData = pScene->mMeshDesc; + auto& instanceData = pScene->mMeshInstanceData; + meshData.resize(mMeshes.size()); + pScene->mMeshHasDynamicData.resize(mMeshes.size()); + + size_t drawCount = 0; + for (uint32_t meshID = 0; meshID < mMeshes.size(); meshID++) + { + // Mesh data + const auto& mesh = mMeshes[meshID]; + meshData[meshID].materialID = mesh.materialId; + meshData[meshID].vbOffset = mesh.staticVertexOffset; + meshData[meshID].ibOffset = mesh.indexOffset; + meshData[meshID].vertexCount = mesh.vertexCount; + meshData[meshID].indexCount = mesh.indexCount; + + drawCount += mesh.instances.size(); + + // Mesh instance data + for (const auto& instance : mesh.instances) + { + instanceData.push_back({}); + auto& meshInstance = instanceData.back(); + meshInstance.globalMatrixID = instance; + meshInstance.materialID = mesh.materialId; + meshInstance.meshID = meshID; + } + + if (mesh.hasDynamicData) + { + assert(mesh.instances.size() == 1); + pScene->mMeshHasDynamicData[meshID] = true; + + for (uint32_t i = 0; i < mesh.vertexCount; i++) + { + mBuffersData.dynamicData[mesh.dynamicVertexOffset + i].globalMatrixID = (uint32_t)mesh.instances[0]; + } + } + } + assert(drawCount <= UINT32_MAX); + return (uint32_t)drawCount; + } + + Scene::SharedPtr SceneBuilder::getScene() + { + if (mMeshes.size() == 0) + { + logError("Can't build scene. No meshes were loaded"); + return nullptr; + } + Scene::SharedPtr pScene = Scene::create(); + if (mCamera.pObject == nullptr) mCamera.pObject = Camera::create(); + pScene->mCamera = mCamera; + pScene->mCameraSpeed = mCameraSpeed; + pScene->mLights = mLights; + pScene->mMaterials = mMaterials; + pScene->mpLightProbe = mpLightProbe; + pScene->mpEnvMap = mpEnvMap; + pScene->mFilename = mFilename; + + createGlobalMatricesBuffer(pScene.get()); + uint32_t drawCount = createMeshData(pScene.get()); + pScene->mpVao = createVao(drawCount); + calculateMeshBoundingBoxes(pScene.get()); + createAnimationController(pScene.get()); + pScene->finalize(); + + return pScene; + } + + void SceneBuilder::calculateMeshBoundingBoxes(Scene* pScene) + { + // Calculate mesh bounding boxes + pScene->mMeshBBs.resize(mMeshes.size()); + for (uint32_t i = 0; i < (uint32_t)mMeshes.size(); i++) + { + const auto& mesh = mMeshes[i]; + vec3 boxMin(FLT_MAX); + vec3 boxMax(-FLT_MAX); + + const auto* staticData = &mBuffersData.staticData[mesh.staticVertexOffset]; + for (uint32_t v = 0; v < mesh.vertexCount; v++) + { + boxMin = glm::min(boxMin, staticData[v].position); + boxMax = glm::max(boxMax, staticData[v].position); + } + + pScene->mMeshBBs[i] = BoundingBox::fromMinMax(boxMin, boxMax); + } + } + + size_t SceneBuilder::addAnimation(size_t meshID, Animation::ConstSharedPtrRef pAnimation) + { + assert(meshID < mMeshes.size()); + mMeshes[meshID].animations.push_back(pAnimation); + return mMeshes[meshID].animations.size() - 1; + } + + void SceneBuilder::createAnimationController(Scene* pScene) + { + pScene->mpAnimationController = AnimationController::create(pScene, mBuffersData.staticData, mBuffersData.dynamicData); + for (uint32_t i = 0; i < mMeshes.size(); i++) + { + for (const auto& pAnim : mMeshes[i].animations) + { + pScene->mpAnimationController->addAnimation(i, pAnim); + } + } + } +} diff --git a/Source/Falcor/Scene/SceneBuilder.h b/Source/Falcor/Scene/SceneBuilder.h new file mode 100644 index 000000000..a762d05ff --- /dev/null +++ b/Source/Falcor/Scene/SceneBuilder.h @@ -0,0 +1,230 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Scene.h" +#include "Data/VertexAttrib.h" + +namespace Falcor +{ + class dlldecl SceneBuilder + { + public: + using SharedPtr = std::shared_ptr; + + /** Flags that control how the scene will be built. They can be combined together + */ + enum class Flags + { + None = 0x0, ///< None + RemoveDuplicateMaterials = 0x1, ///< Deduplicate materials that have the same properties. The material name is ignored during the search + UseOriginalTangentSpace = 0x2, ///< Use the original bitangents that were loaded with the mesh. By default, we will ignore them and use MikkTSpace to generate the tangent space. We will always generate bitangents if they are missing + AssumeLinearSpaceTextures = 0x4, ///< By default, textures representing colors (diffuse/specular) are interpreted as sRGB data. Use this flag to force linear space for color textures. + DontMergeMeshes = 0x8, ///< Preserve the original list of meshes in the scene, don't merge meshes with the same material + BuffersAsShaderResource = 0x10, ///< Generate the VBs and IB with the shader-resource-view bind flag + UseSpecGlossMaterials = 0x20, ///< Set materials to use Spec-Gloss shading model. Otherwise default is Spec-Gloss for OBJ, Metal-Rough for everything else + UseMetalRoughMaterials = 0x40, ///< Set materials to use Metal-Rough shading model. Otherwise default is Spec-Gloss for OBJ, Metal-Rough for everything else + + Default = RemoveDuplicateMaterials + }; + + /** Mesh description + */ + struct Mesh + { + std::string name; // The mesh's name + uint32_t vertexCount = 0; // The number of vertices the mesh has + uint32_t indexCount = 0; // The number of indices the mesh has. Can't be zero - the scene doesn't support non-indexed meshes. If you'd like us to support non-indexed meshes, please open an issue + const uint32_t* pIndices = nullptr; // Array of indices. The element count must match `indexCount` + const vec3* pPositions = nullptr; // Array of vertex positions. The element count must match `vertexCount`. This field is required + const vec3* pNormals = nullptr; // Array of vertex normals. The element count must match `vertexCount`. This field is required + const vec3* pBitangents = nullptr; // Array of vertex bitangent. The element count must match `vertexCount`. Optional. If set to nullptr, or if BuildFlags::UseOriginalTangentSpace is not set, the tangents will be generated using MikkTSpace + const vec2* pTexCrd = nullptr; // Array of vertex texture coordinates. The element count must match `vertexCount`. This field is required + const vec3* pLightMapUVs = nullptr; // Array of light-map UVs. The element count must match `vertexCount`. This field is optional + const uvec4* pBoneIDs = nullptr; // Array of bone IDs. The element count must match `vertexCount`. This field is optional. If it's set, that means that the mesh is animated, in which case pBoneWeights can't be nullptr + const vec4* pBoneWeights = nullptr; // Array of bone weights. The element count must match `vertexCount`. This field is optional. If it's set, that means that the mesh is animated, in which case pBoneIDs can't be nullptr + Vao::Topology topology = Vao::Topology::Undefined; // The primitive topology of the mesh + Material::SharedPtr pMaterial; // The mesh's material. Can't be nullptr + }; + + static const uint32_t kInvalidNode = Scene::kInvalidNode; + struct Node + { + std::string name; + mat4 transform; + mat4 localToBindPose; // For bones + size_t parent = kInvalidNode; + }; + + using InstanceMatrices = std::vector; + + /** Construct a new object + */ + SceneBuilder(Flags buildFlags = Flags::Default); + + /** Create a new object + */ + static SharedPtr create(Flags mFlags = Flags::Default); + + /** Create a new builder and import a scene/model file + \param filename The filename to load + \param flags The build flags + \param instances A list of instance matrices to load. This is optional, by default a single instance will be load + \return A new object with the imported file already initialized. If an import error occurred, a nullptr will be returned + */ + static SharedPtr create(const std::string& filename, Flags buildFlags = Flags::Default, const InstanceMatrices& instances = InstanceMatrices()); + + /** Import a scene/model file + \param filename The filename to load + \param instances A list of instance matrices to load. This is optional, by default a single instance will be load + \return true if the import succeeded, otherwise false + */ + bool import(const std::string& filename, const InstanceMatrices& instances = InstanceMatrices()); + + /** Get the scene. Make sure to add all the objects before calling this function + \return nullptr if something went wrong, otherwise a new Scene object + */ + Scene::SharedPtr getScene(); + + /** Adds a node to the graph + Note that if the node contains data other then the transform matrix (such as meshes or lights), you'll need to add those objects before adding the node. + */ + size_t addNode(const Node& node); + + /** Add a mesh instance to a node + */ + void addMeshInstance(size_t nodeID, size_t meshID); + + /** Add a mesh. This function will throw an exception if something went wrong + \param mesh The mesh's desc + \param flags The build flags + \return The ID of the mesh in the scene. Note that all of the instances share the same mesh ID + */ + size_t addMesh(const Mesh& mesh); + + /** Add a light source + \param pLight The light object. Can't be nullptr + \return The light ID + */ + size_t addLight(const Light::SharedPtr& pLight, size_t nodeID = kInvalidNode); + + /** Get the number of attached lights + */ + size_t getLightCount() const { return mLights.size(); } + + /** Set a light-probe + \param pProbe The environment map. You can set it to null to disable environment mapping + */ + void setLightProbe(LightProbe::ConstSharedPtrRef pProbe) { mpLightProbe = pProbe; } + + /** Set an environment map + */ + void setEnvironmentMap(Texture::ConstSharedPtrRef pEnvMap) { mpEnvMap = pEnvMap; } + + /** Set the camera + */ + void setCamera(const Camera::SharedPtr& pCamera, size_t nodeID = kInvalidNode); + + /** Get the build flags + */ + Flags getFlags() const { return mFlags; } + + /** Add an animation + \param meshID The mesh ID the animation should be applied to + \param animation The animation + \return The ID of the animation. The ID is relative to number of animations which are associated with the specified mesh, it's not a global ID + */ + size_t addAnimation(size_t meshID, Animation::ConstSharedPtrRef pAnimation); + + /** Set the camera's speed + */ + void setCameraSpeed(float speed) { mCameraSpeed = speed; } + + /** Check if a camera exists + */ + bool hasCamera() const { return mCamera.pObject != nullptr; } + private: + struct InternalNode : Node + { + InternalNode() = default; + InternalNode(const Node& n) : Node(n) {} + std::vector children; + std::vector meshes; + }; + + struct MeshSpec + { + MeshSpec() = default; + Vao::Topology topology; + uint32_t materialId = 0; + uint32_t indexOffset = 0; + uint32_t staticVertexOffset = 0; + uint32_t dynamicVertexOffset = 0; + uint32_t indexCount = 0; + uint32_t vertexCount = 0; + bool hasDynamicData = false; + std::vector instances; // Node IDs + std::vector animations; + }; + + // Geometry data + struct BuffersData + { + std::vector indices; + std::vector staticData; + std::vector dynamicData; +// std::vector optionalData; + } mBuffersData; + + using SceneGraph = std::vector; + using MeshList = std::vector; + + SceneGraph mSceneGraph; + Flags mFlags; + + MeshList mMeshes; + std::vector mMaterials; + std::unordered_map mMaterialToId; + + Scene::AnimatedObject mCamera; + std::vector> mLights; + LightProbe::SharedPtr mpLightProbe; + Texture::SharedPtr mpEnvMap; + float mCameraSpeed = 1.0f; + + uint32_t addMaterial(const Material::SharedPtr& pMaterial, bool forceNew); + Vao::SharedPtr createVao(uint16_t drawCount); + + uint32_t createMeshData(Scene* pScene); + void createGlobalMatricesBuffer(Scene* pScene); + void calculateMeshBoundingBoxes(Scene* pScene); + void createAnimationController(Scene* pScene); + std::string mFilename; +}; + + enum_class_operators(SceneBuilder::Flags); +} diff --git a/Source/Falcor/ShaderSource.targets b/Source/Falcor/ShaderSource.targets new file mode 100644 index 000000000..b1d27b533 --- /dev/null +++ b/Source/Falcor/ShaderSource.targets @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + $(BuildDependsOn);ShaderSourceCopy;ShaderSourceCopyFlat + $(CleanDependsOn);ShaderSourceClean;ShaderSourceCleanFlat + + + diff --git a/Source/Falcor/ShaderSource.xml b/Source/Falcor/ShaderSource.xml new file mode 100644 index 000000000..6b436ac04 --- /dev/null +++ b/Source/Falcor/ShaderSource.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Framework/Source/ShadingUtils/BRDF.slang b/Source/Falcor/ShadingUtils/BRDF.slang similarity index 91% rename from Framework/Source/ShadingUtils/BRDF.slang rename to Source/Falcor/ShadingUtils/BRDF.slang index a4c9f3645..ee39b68e4 100644 --- a/Framework/Source/ShadingUtils/BRDF.slang +++ b/Source/Falcor/ShadingUtils/BRDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -36,9 +36,7 @@ __import Lights; #define DiffuseBrdfDisney 1 #define DiffuseBrdfFrostbite 2 -#ifndef DiffuseBrdf #define DiffuseBrdf DiffuseBrdfFrostbite -#endif float3 fresnelSchlick(float3 f0, float3 f90, float u) { @@ -93,29 +91,30 @@ float3 evalDiffuseBrdf(ShadingData sd, LightSample ls) #endif } -float evalGGX(float roughness, float NdotH) +float evalGGX(float ggxAlpha, float NdotH) { - float a2 = roughness * roughness; + float a2 = ggxAlpha * ggxAlpha; float d = ((NdotH * a2 - NdotH) * NdotH + 1); return a2 / (d * d); } -float evalSmithGGX(float NdotL, float NdotV, float roughness) +float evalSmithGGX(float NdotL, float NdotV, float ggxAlpha) { // Optimized version of Smith, already taking into account the division by (4 * NdotV) - float a2 = roughness * roughness; + float a2 = ggxAlpha * ggxAlpha; // `NdotV *` and `NdotL *` are inversed. It's not a mistake. float ggxv = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2); float ggxl = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2); return 0.5f / (ggxv + ggxl); + } float3 evalSpecularBrdf(ShadingData sd, LightSample ls) { - float roughness = sd.roughness; - - float D = evalGGX(roughness, ls.NdotH); - float G = evalSmithGGX(ls.NdotL, sd.NdotV, roughness); + float ggxAlpha = sd.ggxAlpha; + + float D = evalGGX(ggxAlpha, ls.NdotH); + float G = evalSmithGGX(ls.NdotL, sd.NdotV, ggxAlpha); float3 F = fresnelSchlick(sd.specular, 1, max(0, ls.LdotH)); return D * G * F * M_INV_PI; } diff --git a/Framework/Source/ShadingUtils/Helpers.slang b/Source/Falcor/ShadingUtils/Helpers.slang similarity index 69% rename from Framework/Source/ShadingUtils/Helpers.slang rename to Source/Falcor/ShadingUtils/Helpers.slang index 12d5cd52c..4411ab2e6 100644 --- a/Framework/Source/ShadingUtils/Helpers.slang +++ b/Source/Falcor/ShadingUtils/Helpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -73,111 +73,98 @@ float2 getHammersley(uint i, uint N) return float2(float(i) / float(N), radicalInverse(i)); } -// Utility function to get a vector perpendicular to an input vector -// (from "Efficient Construction of Perpendicular Vectors Without Branching") -float3 getPerpendicularStark(float3 u) -{ - float3 a = abs(u); - uint xm = ((a.x - a.y) < 0 && (a.x - a.z) < 0) ? 1 : 0; - uint ym = (a.y - a.z) < 0 ? (1 ^ xm) : 0; - uint zm = 1 ^ (xm | ym); - return cross(u, float3(xm, ym, zm)); -} +/******************************************************************* + Ray tracing +*******************************************************************/ -float3 getPerpendicularSimple(float3 u) -{ - float3 up = abs(u.z) < 0.999999f ? float3(0, 0, 1) : float3(1, 0, 0); - return normalize(cross(up, u)); -} +/** Computes new ray origin based on hit position to avoid self-intersections. + The function assumes that the hit position has been computed by barycentric + interpolation, and not from the ray t which is less accurate. -/** Get a cosine-weighted random vector centered around a specified normal direction. + The method is described in Ray Tracing Gems, Chapter 6, "A Fast and Robust + Method for Avoiding Self-Intersection" by Carsten Wächter and Nikolaus Binder. - \param[in] u Uniformly distributed random numbers between 0 and 1 - \param[in] N Surface normal - \param[in] T A vector perpendicular to N + \param[in] pos Ray hit position. + \param[in] normal Face normal of hit surface (normalized). The offset will be in the positive direction. + \return Ray origin of the new ray. */ -float3 getCosHemisphereSample(float2 u, float3 N, float3 T) +float3 computeRayOrigin(float3 pos, float3 normal) { - float3 B = normalize(cross(N, T)); - - float r = sqrt(u.x); - float phi = u.y * M_PI2; + const float origin = 1.f / 32.f; + const float fScale = 1.f / 65536.f; + const float iScale = 256.f; - float3 L = float3(r * cos(phi), - r * sin(phi), - sqrt(max(0.0f, 1.0f - u.x))); + // Per-component integer offset to bit representation of fp32 position. + int3 iOff = int3(normal * iScale); + float3 iPos = asfloat(asint(pos) + (pos < 0.f ? -iOff : iOff)); - return normalize(T * L.x + B * L.y + N * L.z); + // Select per-component between small fixed offset or above variable offset depending on distance to origin. + float3 fOff = normal * fScale; + return abs(pos) < origin ? pos + fOff : iPos; } -/** Get a GGX half vector / microfacet normal, sampled according to the GGX distribution - When using this function to sample, the probability density is pdf = D * NdotH / (4 * HdotV) - - \param[in] u Uniformly distributed random numbers between 0 and 1 - \param[in] N Surface normal - \param[in] roughness Roughness^2 of material +/** Ray-sphere intersection. + This function implements the standard analytic test with improvements to floating-point precision + and returns the closest hit. + \param[in] rayOrigin Ray origin. + \param[in] rayDir Ray direction (does not have to be normalized). + \param[in] center Sphere center. + \param[in] radius Sphere radius. + \param[in] intersectionPos Position on the sphere for the closest intersection (if any). + \return True if the ray intersects the sphere. */ -float3 getGGXMicrofacet(float2 u, float3 N, float roughness) +bool intersectRaySphere(float3 rayOrigin, float3 rayDir, float3 sphereCenter, float sphereRadius, out float3 intersectionPos) { - float a2 = roughness * roughness; + // Implementation is taken from Chapter 7 of Ray-Tracing Gems + float3 f = rayOrigin - sphereCenter; + float a = dot(rayDir, rayDir); + float b = dot(-f, rayDir); + float discriminant = sphereRadius * sphereRadius - dot(f + b / a * rayDir, f + b / a * rayDir); - float phi = M_PI2 * u.x; - float cosTheta = sqrt(max(0, (1 - u.y)) / (1 + (a2 * a2 - 1) * u.y)); - float sinTheta = sqrt(max(0, 1 - cosTheta * cosTheta)); + // Negative discriminant means ray missed sphere. + if (discriminant < 0.f) return false; - // Tangent space H - float3 tH; - tH.x = sinTheta * cos(phi); - tH.y = sinTheta * sin(phi); - tH.z = cosTheta; + // If b and discriminant are both 0, then the ray's origin lies on the sphere + if (b == 0 && discriminant == 0) + { + intersectionPos = rayOrigin; + return true; + } + + // There are two solutions t0 and t1, but one or both may be negative. + float c = dot(f, f) - sphereRadius * sphereRadius; + float signB = (b < 0) ? -1 : 1; + float q = b + signB * sqrt(a * discriminant); + float t0 = c / q; + float t1 = q / a; - float3 T = getPerpendicularStark(N); - float3 B = normalize(cross(N, T)); + float tc = t0 < 0.f ? t1 : t0; // tc is the closest hit we care about + if (tc < 0.f) return false; - // World space H - return normalize(T * tH.x + B * tH.y + N * tH.z); + intersectionPos = rayOrigin + tc * rayDir; + return true; } /******************************************************************* - Other + Shading *******************************************************************/ -bool intersectRaySphere(float3 rayPos, float3 rayDir, float3 spherePos, float sphereRadius, out float3 intersectionPos) -{ - float3 m = rayPos - spherePos; - float b = dot(m, rayDir); - float c = dot(m, m) - (sphereRadius * sphereRadius); - - // If ray origin is outside sphere (c > 0) and ray is pointing away from sphere (b > 0) - // For now assume input always produces valid intersection - // if(c > 0.0f && b > 0.0f) return false; - - float discr = b * b - c; - // Negative discriminant means ray missed sphere - // if(discr < 0.0f) return false; - - float t = -b - sqrt(discr); - - // t will be negative if origin is inside sphere, - // take the abs since we want the position on the sphere's surface - intersectionPos = rayPos + abs(t) * rayDir; - - return true; -} - float4 applyAmbientOcclusion(float4 color, Texture2D aoTex, SamplerState s, float2 texC) { float aoFactor = aoTex.SampleLevel(s, texC, 0).r; return float4(color.rgb * aoFactor, color.a); } -float getMetallic(float diffuse, float spec) +float getMetallic(float3 diffuse, float3 spec) { // This is based on the way that UE4 and Substance Painter 2 converts base+metallness+specular level to diffuse/spec colors // We don't have the specular level information, so the assumption is that it is equal to 0.5 (based on the UE4 documentation) + // Note that I'm using the luminance here instead of the actual colors. The reason is that there's no guaraentee that all RGB channels will end up with the same metallic value + float d = luminance(diffuse); + float s = luminance(spec); float a = 0.04; - float b = spec + diffuse - 0.08; - float c = 0.04 - spec; + float b = s + d - 0.08; + float c = 0.04 - s; float root = sqrt(b*b - 0.16*c); float m = (root - b) * 12.5; return m; @@ -191,7 +178,6 @@ bool evalBasicAlphaTest(float alpha, float threshold) { return alpha < threshold; } - /******************************************************************* Hashed Alpha Test *******************************************************************/ @@ -224,36 +210,30 @@ float calculateHashedAlpha(float3 hashInputCoord, float hashScale, bool useAniso // Find the discretized derivatives of our coordinates float3 anisoDeriv = max(abs(ddx(hashInputCoord)), abs(ddy(hashInputCoord))); float3 anisoScales = float3(0.707f / (hashScale * anisoDeriv.x), - 0.707f / (hashScale * anisoDeriv.y), - 0.707f / (hashScale * anisoDeriv.z)); + 0.707f / (hashScale * anisoDeriv.y), + 0.707f / (hashScale * anisoDeriv.z)); // Find log-discretized noise scales float3 scaleFlr = float3(exp2(floor(log2(anisoScales.x))), - exp2(floor(log2(anisoScales.y))), - exp2(floor(log2(anisoScales.z)))); + exp2(floor(log2(anisoScales.y))), + exp2(floor(log2(anisoScales.z)))); float3 scaleCeil = float3(exp2(ceil(log2(anisoScales.x))), - exp2(ceil(log2(anisoScales.y))), - exp2(ceil(log2(anisoScales.z)))); - + exp2(ceil(log2(anisoScales.y))), + exp2(ceil(log2(anisoScales.z)))); // Compute alpha thresholds at our two noise scales float2 alpha = float2(sineHash3D(floor(scaleFlr * hashInputCoord)), - sineHash3D(floor(scaleCeil * hashInputCoord))); - + sineHash3D(floor(scaleCeil * hashInputCoord))); // Factor to linearly interpolate with float3 fractLoc = float3(frac(log2(anisoScales.x)), - frac(log2(anisoScales.y)), - frac(log2(anisoScales.z))); - - float2 toCorners = float2(length(fractLoc), - length(float3(1.0f, 1.0f, 1.0f) - fractLoc)); + frac(log2(anisoScales.y)), + frac(log2(anisoScales.z))); + float2 toCorners = float2(length(fractLoc), + length(float3(1.0f, 1.0f, 1.0f) - fractLoc)); float lerpFactor = toCorners.x / (toCorners.x + toCorners.y); - // Interpolate alpha threshold from noise at two scales float x = (1 - lerpFactor) * alpha.x + lerpFactor * alpha.y; - // Pass into CDF to compute uniformly distrib threshold float a = min(lerpFactor, 1 - lerpFactor); float3 cases = float3(x * x / (2 * a * (1 - a)), (x - 0.5 * a) / (1 - a), 1.0 - ((1 - x) * (1 - x) / (2 * a * (1 - a)))); - // Find our final, uniformly distributed alpha threshold alphaCompare = (x < (1 - a)) ? ((x < a) ? cases.x : cases.y) : cases.z; alphaCompare = clamp(alphaCompare, 1.0e-6, 1.0f); @@ -265,23 +245,17 @@ float calculateHashedAlpha(float3 hashInputCoord, float hashScale, bool useAniso // Find the discretized derivatives of our coordinates float maxDeriv = max(length(ddx(hashInputCoord)), length(ddy(hashInputCoord))); float pixScale = 1.0 / (hashScale * maxDeriv); - // Find two nearest log-discretized noise scales float2 pixScales = float2(exp2(floor(log2(pixScale))), exp2(ceil(log2(pixScale)))); - // Compute alpha thresholds at our two noise scales float2 alpha = float2(sineHash3D(floor(pixScales.x * hashInputCoord)), sineHash3D(floor(pixScales.y * hashInputCoord))); - // Factor to interpolate lerp with float lerpFactor = frac(log2(pixScale)); - // Interpolate alpha threshold from noise at two scales float x = (1 - lerpFactor) * alpha.x + lerpFactor * alpha.y; float a = min(lerpFactor, 1 - lerpFactor); - // Pass into CDF to compute uniformly distrib threshold float3 cases = float3(x * x / (2 * a * (1 - a)), (x - 0.5 * a) / (1 - a), 1.0 - ((1 - x) * (1 - x) / (2 * a * (1 - a)))); - // Find our final, uniformly distributed alpha threshold alphaCompare = (x < (1 - a)) ? ((x < a) ? cases.x : cases.y) : cases.z; alphaCompare = clamp(alphaCompare, 1e-6f, 1.0f); diff --git a/Framework/Source/ShadingUtils/Lights.slang b/Source/Falcor/ShadingUtils/Lights.slang similarity index 86% rename from Framework/Source/ShadingUtils/Lights.slang rename to Source/Falcor/ShadingUtils/Lights.slang index ecbf611d1..5e80dfb48 100644 --- a/Framework/Source/ShadingUtils/Lights.slang +++ b/Source/Falcor/ShadingUtils/Lights.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,6 +30,7 @@ #include "HostDeviceData.h" __import ShaderCommon; __import Helpers; +__import Scene; struct LightSample { @@ -46,9 +47,9 @@ struct LightSample void calcCommonLightProperties(ShadingData sd, inout LightSample ls) { float3 H = normalize(sd.V + ls.L); - ls.NdotH = saturate(dot(sd.N, H)); - ls.NdotL = saturate(dot(sd.N, ls.L)); - ls.LdotH = saturate(dot(ls.L, H)); + ls.NdotH = dot(sd.N, H); + ls.NdotL = dot(sd.N, ls.L); + ls.LdotH = dot(ls.L, H); }; float getDistanceFalloff(float distSquared) @@ -87,11 +88,11 @@ LightSample evalPointLight(in LightData light, in float3 surfacePosW) // Calculate the falloff for spot-lights float cosTheta = -dot(ls.L, light.dirW); // cos of angle of light orientation - if(cosTheta < light.cosOpeningAngle) + if (cosTheta < light.cosOpeningAngle) { falloff = 0; } - else if(light.penumbraAngle > 0) + else if (light.penumbraAngle > 0) { float deltaAngle = light.openingAngle - acos(cosTheta); falloff *= saturate((deltaAngle - light.penumbraAngle) / light.penumbraAngle); @@ -137,24 +138,24 @@ LightSample evalLight(LightData light, ShadingData sd) return ls; }; -float3 getDiffuseDominantDir(float3 N, float3 V, float roughness) +float3 getDiffuseDominantDir(float3 N, float3 V, float ggxAlpha) { - float a = 1.02341 * roughness - 1.51174; - float b = -0.511705 * roughness + 0.755868; - float factor = saturate((saturate(dot(N, V)) * a + b) * roughness); + float a = 1.02341 * ggxAlpha - 1.51174; + float b = -0.511705 * ggxAlpha + 0.755868; + float factor = saturate((saturate(dot(N, V)) * a + b) * ggxAlpha); return normalize(lerp(N, V, factor)); } -float3 getSpecularDominantDir(float3 N, float3 R, float roughness) +float3 getSpecularDominantDir(float3 N, float3 R, float ggxAlpha) { - float smoothness = 1 - roughness; - float factor = smoothness * (sqrt(smoothness) + roughness); + float smoothness = 1 - ggxAlpha; + float factor = smoothness * (sqrt(smoothness) + ggxAlpha); return normalize(lerp(N, R, factor)); } float3 evalLightProbeDiffuse(LightProbeData probe, ShadingData sd) { - float3 N = getDiffuseDominantDir(sd.N, sd.V, sd.roughness); + float3 N = getDiffuseDominantDir(sd.N, sd.V, sd.ggxAlpha); // Interpret negative radius as global light probe with infinite distance // Otherwise simulate the light probe as covering a finite spherical area @@ -174,7 +175,7 @@ float3 evalLightProbeDiffuse(LightProbeData probe, ShadingData sd) probe.resources.diffuseTexture.GetDimensions(0, width, height, mipCount); float3 diffuseLighting = probe.resources.diffuseTexture.SampleLevel(probe.resources.sampler, uv, 0).rgb; - float preintegratedDisneyBRDF = gProbeShared.dfgTexture.SampleLevel(gProbeShared.dfgSampler, float2(sd.NdotV, sd.roughness), 0).z; + float preintegratedDisneyBRDF = gScene.probeShared.dfgTexture.SampleLevel(gScene.probeShared.dfgSampler, float2(sd.NdotV, sd.ggxAlpha), 0).z; return diffuseLighting * preintegratedDisneyBRDF * sd.diffuse.rgb; } @@ -182,13 +183,13 @@ float3 evalLightProbeDiffuse(LightProbeData probe, ShadingData sd) float3 evalLightProbeSpecular(LightProbeData probe, ShadingData sd, float3 L) { float dfgWidth, dfgHeight; - gProbeShared.dfgTexture.GetDimensions(dfgWidth, dfgHeight); + gScene.probeShared.dfgTexture.GetDimensions(dfgWidth, dfgHeight); float width, height, mipCount; probe.resources.specularTexture.GetDimensions(0, width, height, mipCount); - float3 dominantDir = getSpecularDominantDir(sd.N, L, sd.roughness); - float mipLevel = linearRoughnessToLod(sd.roughness, mipCount); + float3 dominantDir = getSpecularDominantDir(sd.N, L, sd.ggxAlpha); + float mipLevel = linearRoughnessToLod(sd.ggxAlpha, mipCount); float2 uv; if (probe.radius < 0.0f) @@ -204,7 +205,7 @@ float3 evalLightProbeSpecular(LightProbeData probe, ShadingData sd, float3 L) float3 ld = probe.resources.specularTexture.SampleLevel(probe.resources.sampler, uv, mipLevel).rgb; - float2 dfg = gProbeShared.dfgTexture.SampleLevel(gProbeShared.dfgSampler, float2(sd.NdotV, sd.roughness), 0).xy; + float2 dfg = gScene.probeShared.dfgTexture.SampleLevel(gScene.probeShared.dfgSampler, float2(sd.NdotV, sd.ggxAlpha), 0).xy; // ld * (f0 * Gv * (1 - Fc)) + (f90 * Gv * Fc) return ld * (sd.specular * dfg.x + dfg.y); diff --git a/Source/Falcor/ShadingUtils/Raytracing.slang b/Source/Falcor/ShadingUtils/Raytracing.slang new file mode 100644 index 000000000..9b52f505b --- /dev/null +++ b/Source/Falcor/ShadingUtils/Raytracing.slang @@ -0,0 +1,121 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +__exported import Helpers; +__exported import Shading; +import Scene; + +shared RaytracingAccelerationStructure gRtScene : register(t52); +shared ByteAddressBuffer gAsToInstance : register(t53); + +shared cbuffer DxrPerFrame : register(b13) +{ + uint hitProgramCount; +}; + +cbuffer DxrPerGeometry : register(b14) +{ + uint geometryIndex; +}; + +/** Returns the global hit ID (= mesh instance ID in the scene). + This function can only be called from a ray tracing hit program. + \return Global hit ID. +*/ +uint getGlobalHitID() +{ + uint address = (InstanceID() + geometryIndex) * 4; + return gAsToInstance.Load(address); +} + +/** Returns interpolated vertex attributes in a ray tracing hit program. + \param[in] triangleIndex Index of the triangle in the current mesh (= PrimitiveIndex()). + \param[in] attribs Intersection attributes provided by DXR. + \return Interpolated vertex attributes. +*/ +VertexData getVertexData(uint triangleIndex, BuiltInTriangleIntersectionAttributes attribs) +{ + float3 barycentrics = float3(1.0 - attribs.barycentrics.x - attribs.barycentrics.y, attribs.barycentrics.x, attribs.barycentrics.y); + return gScene.getVertexData(getGlobalHitID(), triangleIndex, barycentrics); +} + +/** Returns interpolated position on a triangle in world space for the previous frame. + \param[in] triangleIndex Index of the triangle in the current mesh (= PrimitiveIndex()). + \param[in] attribs Intersection attributes provided by DXR. + \return Interpolated position in world space for the previous frame. +*/ +float3 getPrevPosW(uint triangleIndex, BuiltInTriangleIntersectionAttributes attribs) +{ + float3 barycentrics = float3(1.0 - attribs.barycentrics.x - attribs.barycentrics.y, attribs.barycentrics.x, attribs.barycentrics.y); + return gScene.getPrevPosW(getGlobalHitID(), triangleIndex, barycentrics); +} + + +// TODO: Move below functions into HelloDXR.rt.hlsl where they are used? +// They make assumptions that are not generally applicable (pseudorandom numbers, jitter etc.). + +/** Generates a ray given a set of camera parameters, the viewport dimensions, and the location of the pixel to be sampled. + Code based on Chris Wyman's raytracing tutorial found here: http://cwyman.org/code/dxrTutors/tutors/Tutor4/tutorial04.md.html +*/ +RayDesc generateRay(const CameraData camera, uint2 posS, float2 viewportDims) +{ + RayDesc r; + r.Origin = camera.posW; + + float2 ndc = (((posS + 0.5) / viewportDims) * 2.f - 1.f); + float3 dir = normalize(ndc.x * camera.cameraU - ndc.y * camera.cameraV + camera.cameraW); + r.Direction = dir; + + float invCos = 1.f / dot(normalize(camera.cameraW), dir); + r.TMin = camera.nearZ * invCos; + r.TMax = camera.farZ * invCos; + return r; +} + +/** Generates a ray from a random position on the camera lens. Code for ray and pseudorandom number generation + heavily based on Chris Wyman's tutorial found here: http://cwyman.org/code/dxrTutors/tutors/Tutor8/tutorial08.md.html +*/ +RayDesc generateDOFRay(const CameraData camera, uint2 posS, float2 viewportDims, inout uint randSeed) +{ + float2 ndc = (((posS + float2(camera.jitterX, camera.jitterY)) / viewportDims) * 2.f - 1.f); + float3 dir = ndc.x * camera.cameraU - ndc.y * camera.cameraV + camera.cameraW; + dir /= length(camera.cameraW); + float3 focalPoint = camera.posW + camera.focalDistance * dir; + + float2 rnd = float2(2.0f * 3.1415926f * rand_next(randSeed), camera.apertureRadius * rand_next(randSeed)); + float2 uv = float2(cos(rnd.x) * rnd.y, sin(rnd.x) * rnd.y); + float3 randomOrig = camera.posW + uv.x * normalize(camera.cameraU) - uv.y * normalize(camera.cameraV); + + RayDesc r; + r.Origin = randomOrig; + r.Direction = normalize(focalPoint - randomOrig); + + float invCos = 1.f / dot(normalize(camera.cameraW), r.Direction); + r.TMin = camera.nearZ * invCos; + r.TMax = camera.farZ * invCos; + return r; +} diff --git a/Source/Falcor/ShadingUtils/Scene.slang b/Source/Falcor/ShadingUtils/Scene.slang new file mode 100644 index 000000000..078df106b --- /dev/null +++ b/Source/Falcor/ShadingUtils/Scene.slang @@ -0,0 +1,337 @@ +/*************************************************************************** +# Copyright (c) 201*, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "HostDeviceData.h" + +// Local stuff +struct LocalMesh +{ + uint meshID; + uint meshInstanceID; + uint materialID; +}; + +#ifndef MATERIAL_COUNT +#define MATERIAL_COUNT 1 +#endif + +/** Data required for rendering +*/ +struct Scene +{ + StructuredBuffer meshInstances; + StructuredBuffer meshes; + StructuredBuffer lights; + + Buffer worldMatrices; + Buffer inverseTransposeWorldMatrices; // #SCENEV2 Should this be 3x3? + Buffer previousFrameWorldMatrices; + MaterialData materials[MATERIAL_COUNT]; + LightProbeData lightProbe; + LightProbeSharedResources probeShared; // Should this be its own parameter block? Doesn't really fit here, and Lights.slang currently needs to import Scene + CameraData camera; + Texture2D envMap; + + StructuredBuffer vertices; + ByteAddressBuffer indices; + + float4x4 getWorldMatrix(uint meshInstanceID) + { + uint matrixID = meshInstances[meshInstanceID].globalMatrixID; + + float4x4 m = + { + worldMatrices[matrixID * 4 + 0], + worldMatrices[matrixID * 4 + 1], + worldMatrices[matrixID * 4 + 2], + worldMatrices[matrixID * 4 + 3] + }; + return m; + }; + + float4x4 getInverseTransposeWorldMatrix(uint meshInstanceID) + { + uint matrixID = meshInstances[meshInstanceID].globalMatrixID; + + float4x4 m = + { + inverseTransposeWorldMatrices[matrixID * 4 + 0], + inverseTransposeWorldMatrices[matrixID * 4 + 1], + inverseTransposeWorldMatrices[matrixID * 4 + 2], + inverseTransposeWorldMatrices[matrixID * 4 + 3] + }; + + return m; + }; + + float4x4 getPrevWorldMatrix(uint meshInstanceID) + { + uint matrixID = meshInstances[meshInstanceID].globalMatrixID; + + float4x4 m = + { + previousFrameWorldMatrices[matrixID * 4 + 0], + previousFrameWorldMatrices[matrixID * 4 + 1], + previousFrameWorldMatrices[matrixID * 4 + 2], + previousFrameWorldMatrices[matrixID * 4 + 3] + }; + return m; + }; + + uint getMaterialID(uint meshInstanceID) + { + return meshInstances[meshInstanceID].materialID; + }; + + uint getMaterialCount() + { + return MATERIAL_COUNT; + } + + MaterialData getMaterial(uint meshInstanceID) + { + return materials[getMaterialID(meshInstanceID)]; + } + + MeshDesc getMeshDesc(uint meshInstanceID) + { + return meshes[meshInstances[meshInstanceID].meshID]; + } + + uint getLightCount() + { + uint count, stride; + lights.GetDimensions(count, stride); + return count; + } + + LightData getLight(uint lightIndex) + { + return lights[lightIndex]; + } + + bool isWorldMatrixFlippedWinding(uint meshInstanceID) + { + return (meshInstances[meshInstanceID].flags & uint(MeshInstanceFlags::Flipped)) != 0; + } + + + // Geometry access + + /** Returns the global vertex indices for a given triangle. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \return Vertex indices into the global vertex buffer. + */ + uint3 getIndices(uint meshInstanceID, uint triangleIndex) + { + uint baseIndex = getMeshDesc(meshInstanceID).ibOffset + (triangleIndex * 3); + uint3 vtxIndices = indices.Load3(baseIndex * 4); + vtxIndices += getMeshDesc(meshInstanceID).vbOffset; + return vtxIndices; + } + + /** Returns a triangle's face normal in object space. + \param[in] vtxIndices Indices into the scene's global vertex buffer. + \param[out] Face normal in object space (normalized). Front facing for counter-clockwise winding. + */ + float3 getFaceNormalInObjectSpace(uint3 vtxIndices) + { + float3 p0 = vertices[vtxIndices[0]].position; + float3 p1 = vertices[vtxIndices[1]].position; + float3 p2 = vertices[vtxIndices[2]].position; + return normalize(cross(p1 - p0, p2 - p0)); + } + + /** Returns a triangle's face normal in world space. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[out] Face normal in world space (normalized). + */ + float3 getFaceNormalW(uint meshInstanceID, uint triangleIndex) + { + uint3 vtxIndices = getIndices(meshInstanceID, triangleIndex); + float3 p0 = vertices[vtxIndices[0]].position; + float3 p1 = vertices[vtxIndices[1]].position; + float3 p2 = vertices[vtxIndices[2]].position; + float3 N = cross(p1 - p0, p2 - p0); + float3x3 worldInvTransposeMat = (float3x3) getInverseTransposeWorldMatrix(meshInstanceID); + return normalize(mul(N, worldInvTransposeMat)); + } + + /** Returns a triangle's area in world space. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[out] Triangle area. + */ + float getFaceAreaW(uint meshInstanceID, uint triangleIndex) + { + float3 p[3]; + getVertexPositionsW(meshInstanceID, triangleIndex, p); + return 0.5f * length(cross(p[1] - p[0], p[2] - p[0])); + } + + /** Computes the face normal and area of a triangle given its vertices. + \param[in] meshInstanceID The mesh instance ID. + \param[in] p Position of vertex 0,1,2 in world space. + \param[out] triangleArea Triangle area in world space units. + \return Face normal in world space (normalized). + */ + float3 computeFaceNormalAndAreaW(uint meshInstanceID, const float3 p[3], out float triangleArea) + { + // Compute face normal in world space. + // The length of the vector is twice the triangle area since we're in world space. + // Note that this is not true if the normal is transformed using the inverse-transpose. + float3 e[2]; + e[0] = p[1] - p[0]; + e[1] = p[2] - p[0]; + float3 N = cross(e[0], e[1]); + triangleArea = 0.5f * length(N); + + // Flip the normal if the instance transform changed the handedness of the coordinate system. + if (isWorldMatrixFlippedWinding(meshInstanceID)) N = -N; + + return normalize(N); + } + + /** Returns a triangle's face normal and area in world space. + This function should only be used if the triangle area is needed, as it is less efficient than computing just its normal. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[out] triangleArea Triangle area in world space units. + \return Face normal in world space (normalized). + */ + float3 getFaceNormalAndAreaW(uint meshInstanceID, uint triangleIndex, out float triangleArea) + { + uint3 vtxIndices = getIndices(meshInstanceID, triangleIndex); + + // Load vertices and transform to world space. + float3 p[3]; + [unroll] + for (int i = 0; i < 3; i++) + { + p[i] = vertices[vtxIndices[i]].position; + p[i] = mul(float4(p[i], 1.f), getWorldMatrix(meshInstanceID)).xyz; + } + + return computeFaceNormalAndAreaW(meshInstanceID, p, triangleArea); + } + + /** Returns the interpolated vertex attributes for a given triangle. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[in] barycentrics Barycentric coordinates in the triangle. + \return Interpolated vertex attributes. + */ + VertexData getVertexData(uint meshInstanceID, uint triangleIndex, float3 barycentrics) + { + const uint3 vtxIndices = getIndices(meshInstanceID, triangleIndex); + VertexData v = {}; + + [unroll] + for (int i = 0; i < 3; i++) + { + v.posW += vertices[vtxIndices[i]].position * barycentrics[i]; + v.normalW += vertices[vtxIndices[i]].normal * barycentrics[i]; + v.bitangentW += vertices[vtxIndices[i]].bitangent * barycentrics[i]; + v.texC += vertices[vtxIndices[i]].texCrd * barycentrics[i]; + } + v.faceNormalW = getFaceNormalInObjectSpace(vtxIndices); + + float4x4 worldMat = getWorldMatrix(meshInstanceID); + float3x3 worldInvTransposeMat = (float3x3) getInverseTransposeWorldMatrix(meshInstanceID); + + v.posW = mul(float4(v.posW, 1.f), worldMat).xyz; + v.normalW = mul(v.normalW, worldInvTransposeMat).xyz; + v.faceNormalW = mul(v.faceNormalW, worldInvTransposeMat).xyz; + v.bitangentW = mul(v.bitangentW, (float3x3)worldMat).xyz; + + v.normalW = normalize(v.normalW); + v.faceNormalW = normalize(v.faceNormalW); + // Handle invalid bitangents gracefully (avoid NaN from normalization). + v.bitangentW = dot(v.bitangentW, v.bitangentW) > 0.f ? normalize(v.bitangentW) : float3(0, 0, 0); + + return v; + } + + /** Returns interpolated position on a triangle in world space for the previous frame. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[in] barycentrics Barycentric coordinates in the triangle. + \return Interpolated position in world space for the previous frame. + */ + float3 getPrevPosW(uint meshInstanceID, uint triangleIndex, float3 barycentrics) + { + const uint3 vtxIndices = getIndices(meshInstanceID, triangleIndex); + float3 prevPos = float3(0, 0, 0); + + [unroll] + for (int i = 0; i < 3; i++) + { + prevPos += vertices[vtxIndices[i]].prevPosition * barycentrics[i]; + } + + float4x4 prevWorldMat = getPrevWorldMatrix(meshInstanceID); + return mul(float4(prevPos, 1.f), prevWorldMat).xyz; + } + + /** Returns a triangle's vertex positions in world space. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[out] p Position of vertex 0,1,2 in world space. + */ + void getVertexPositionsW(uint meshInstanceID, uint triangleIndex, out float3 p[3]) + { + uint3 vtxIndices = getIndices(meshInstanceID, triangleIndex); + float4x4 worldMat = getWorldMatrix(meshInstanceID); + + [unroll] + for (int i = 0; i < 3; i++) + { + p[i] = vertices[vtxIndices[i]].position; + p[i] = mul(float4(p[i], 1.f), worldMat).xyz; + } + } + + /** Returns a triangle's texture coordinates. + \param[in] meshInstanceID The mesh instance ID. + \param[in] triangleIndex Index of the triangle in the given mesh. + \param[out] texC Texture coordinate of vertex 0,1,2. + */ + void getVertexTexCoords(uint meshInstanceID, uint triangleIndex, out float2 texC[3]) + { + uint3 vtxIndices = getIndices(meshInstanceID, triangleIndex); + + [unroll] + for (int i = 0; i < 3; i++) + { + texC[i] = vertices[vtxIndices[i]].texCrd; + } + } +}; + +shared ParameterBlock gScene; diff --git a/Framework/Source/ShadingUtils/Shading.slang b/Source/Falcor/ShadingUtils/Shading.slang similarity index 76% rename from Framework/Source/ShadingUtils/Shading.slang rename to Source/Falcor/ShadingUtils/Shading.slang index db1f52e71..b0531ce31 100644 --- a/Framework/Source/ShadingUtils/Shading.slang +++ b/Source/Falcor/ShadingUtils/Shading.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#ifndef _FALCOR_SHADING_SLANG_ -#define _FALCOR_SHADING_SLANG_ #include "HostDeviceData.h" -__exported __import DefaultVS; -__exported __import ShaderCommon; -__import Lights; -__import BRDF; -__import Helpers; +__exported import ShaderCommon; +import Lights; +import BRDF; +import Helpers; /** Interface for texture sampling techniques. @@ -88,8 +85,8 @@ struct ExplicitGradientTextureSampler : ITextureSampler */ float4 sampleTexture(Texture2D t, SamplerState s, float2 uv, float4 factor, uint mode, L lod) { - if(mode == ChannelTypeUnused) return 0; - if(mode == ChannelTypeConst) return factor; + if (mode == ChannelTypeUnused) return 0; + if (mode == ChannelTypeConst) return factor; // else mode == ChannelTypeTexture return lod.sampleTexture(t, s, uv); } @@ -134,7 +131,7 @@ float3 RgToNormal(float2 rg) void applyNormalMap(MaterialData m, inout ShadingData sd, L lod) { uint mapType = EXTRACT_NORMAL_MAP_TYPE(m.flags); - if(mapType == NormalMapUnused) return; + if (mapType == NormalMapUnused) return; float3 mapN = lod.sampleTexture(m.resources.normalMap, m.resources.samplerState, sd.uv).xyz; switch(mapType) @@ -159,7 +156,7 @@ void applyNormalMap(MaterialData m, inout ShadingData sd, L l The `lod` parameter represents the method to use for computing texture level of detail, and must implement the `ITextureSampler` interface. \return True if hit should be ignored/discarded. */ -bool _alphaTest(VertexOut v, MaterialData m, L lod) +bool _alphaTest(VertexData v, MaterialData m, L lod) { if (EXTRACT_ALPHA_MODE(m.flags) != AlphaModeMask) return false; @@ -172,7 +169,7 @@ bool _alphaTest(VertexOut v, MaterialData m, L lod) This version samples alpha using implicit gradients and only works in pixel shaders. \return True if hit should be ignored/discarded. */ -bool alphaTest(VertexOut v, MaterialData m) +bool alphaTest(VertexData v, MaterialData m) { ImplicitLodTextureSampler lod = {}; return _alphaTest(v, m, lod); @@ -182,7 +179,7 @@ bool alphaTest(VertexOut v, MaterialData m) This version samples alpha at a level of detail specified by the `lod` parameter. \return True if hit should be ignored/discarded. */ -bool alphaTest(VertexOut v, MaterialData m, float lod) +bool alphaTest(VertexData v, MaterialData m, float lod) { ExplicitLodTextureSampler explicitLOD = { lod }; return _alphaTest(v, m, explicitLOD); @@ -192,7 +189,7 @@ bool alphaTest(VertexOut v, MaterialData m, float lod) This version samples alpha at a level of detail computed from screen-space gradients `gradX` and `gradY`. \return True if hit should be ignored/discarded. */ -bool alphaTest(VertexOut v, MaterialData m, float2 gradX, float2 gradY) +bool alphaTest(VertexData v, MaterialData m, float2 gradX, float2 gradY) { ExplicitGradientTextureSampler lod = { gradX, gradY }; return _alphaTest(v, m, lod); @@ -206,39 +203,14 @@ void applyAlphaTest(uint matFlags, float alpha, float threshold, float3 posW) #endif } -ShadingData initShadingData() -{ - ShadingData sd; - sd.posW = 0; - sd.V = 0; - sd.N = 0; - sd.T = 0; - sd.B = 0; - sd.uv = 0; - sd.NdotV = 0; - sd.diffuse = 0; - sd.opacity = 1; - sd.specular = 0; - sd.linearRoughness = 0; - sd.roughness = 0; - sd.emissive = 0; - sd.occlusion = 1; - sd.lightMap = 0; - sd.height = 0; - sd.IoR = 0; - sd.doubleSidedMaterial = false; - - return sd; -} - /** Internal implementation of `prepareShadingData` -The `lod` parameter represents the method to use for computing -texture level of detail, and must implement the `ITextureSampler` interface. + The `lod` parameter represents the method to use for computing + texture level of detail, and must implement the `ITextureSampler` interface. */ -ShadingData _prepareShadingData(VertexOut v, MaterialData m, float3 camPosW, L lod) +ShadingData _prepareShadingData(VertexData v, MaterialData m, float3 camPosW, L lod, bool useNormalMap) { - ShadingData sd = initShadingData(); + ShadingData sd = {}; #ifdef _MS_STATIC_MATERIAL_FLAGS m.flags = _MS_STATIC_MATERIAL_FLAGS; @@ -254,21 +226,31 @@ ShadingData _prepareShadingData(VertexOut v, MaterialData m, sd.V = normalize(camPosW - v.posW); sd.N = normalize(v.normalW); - sd.B = normalize(v.bitangentW - sd.N * (dot(v.bitangentW, sd.N))); - sd.T = normalize(cross(sd.B, sd.N)); + sd.faceN = v.faceNormalW; + sd.frontFacing = dot(sd.V, sd.faceN) >= 0.f; + sd.doubleSided = EXTRACT_DOUBLE_SIDED(m.flags); + + // Check that bitangent exists, otherwise leave the vectors at zero to avoid NaNs. + const bool validTangentSpace = dot(v.bitangentW, v.bitangentW) > 0.f; + if (validTangentSpace) + { + sd.B = normalize(v.bitangentW - sd.N * (dot(v.bitangentW, sd.N))); + sd.T = normalize(cross(sd.B, sd.N)); + } // Sample the spec texture + sd.occlusion = 1.0f; bool sampleOcclusion = EXTRACT_OCCLUSION_MAP(m.flags) > 0; float4 spec = sampleTexture(m.resources.specular, m.resources.samplerState, v.texC, m.specular, EXTRACT_SPECULAR_TYPE(m.flags), lod); if (EXTRACT_SHADING_MODEL(m.flags) == ShadingModelMetalRough) { - // R - Occlusion; G - Roughness; B - Metalness + // R - Occlusion; G - Roughness; B - Metallic sd.diffuse = lerp(baseColor.rgb, float3(0), spec.b); // UE4 uses 0.08 multiplied by a default specular value of 0.5 as a base, hence the 0.04 - sd.specular = lerp(float3(0.04f), baseColor.rgb, spec.b); + sd.specular = lerp(float3(0.04), baseColor.rgb, spec.b); sd.linearRoughness = spec.g; - + sd.metallic = spec.b; if (sampleOcclusion) sd.occlusion = spec.r; } else // if (EXTRACT_SHADING_MODEL(m.flags) == ShadingModelSpecGloss) @@ -276,30 +258,36 @@ ShadingData _prepareShadingData(VertexOut v, MaterialData m, sd.diffuse = baseColor.rgb; sd.specular = spec.rgb; sd.linearRoughness = 1 - spec.a; + sd.metallic = getMetallic(sd.diffuse, sd.specular); - if(sampleOcclusion) + if (sampleOcclusion) { sd.occlusion = sampleTexture(m.resources.occlusionMap, m.resources.samplerState, v.texC, 1, ChannelTypeTexture, lod); } } sd.linearRoughness = max(0.08, sd.linearRoughness); // Clamp the roughness so that the BRDF won't explode - sd.roughness = sd.linearRoughness * sd.linearRoughness; - sd.emissive = sampleTexture(m.resources.emissive, m.resources.samplerState, v.texC, float4(m.emissive, 1), EXTRACT_EMISSIVE_TYPE(m.flags), lod).rgb; + sd.ggxAlpha = sd.linearRoughness * sd.linearRoughness; + + // Sample the emissive texture. Note that triangles are emissive only on the front-facing side. + if (sd.frontFacing) + { + sd.emissive = sampleTexture(m.resources.emissive, m.resources.samplerState, v.texC, float4(m.emissive, 1), EXTRACT_EMISSIVE_TYPE(m.flags), lod).rgb * m.emissiveFactor; + } + sd.IoR = m.IoR; - sd.doubleSidedMaterial = EXTRACT_DOUBLE_SIDED(m.flags); #define channel_type(extract) (extract(m.flags) ? ChannelTypeTexture : ChannelTypeUnused) - sd.lightMap = sampleTexture(m.resources.lightMap, m.resources.samplerState, v.lightmapC, 1, channel_type(EXTRACT_LIGHT_MAP), lod).rgb; + //sd.lightMap = sampleTexture(m.resources.lightMap, m.resources.samplerState, v.lightmapC, 1, channel_type(EXTRACT_LIGHT_MAP), lod).rgb; sd.height = sampleTexture(m.resources.heightMap, m.resources.samplerState, v.texC, 1, channel_type(EXTRACT_HEIGHT_MAP), lod).xy; sd.height = sd.height * m.heightScaleOffset.x + m.heightScaleOffset.y; #undef channel_type - applyNormalMap(m, sd, lod); + if (useNormalMap && validTangentSpace) applyNormalMap(m, sd, lod); sd.NdotV = dot(sd.N, sd.V); - // Flip the normal if it's backfacing - if(sd.NdotV <= 0 && sd.doubleSidedMaterial) + // Flip the shading normal for back-facing hits on double-sided materials. + if (!sd.frontFacing && sd.doubleSided) { sd.N = -sd.N; sd.NdotV = -sd.NdotV; @@ -310,33 +298,33 @@ ShadingData _prepareShadingData(VertexOut v, MaterialData m, /** Prepare the hit-point data */ -ShadingData prepareShadingData(VertexOut v, MaterialData m, float3 camPosW) +ShadingData prepareShadingData(VertexData v, MaterialData m, float3 camPosW) { ImplicitLodTextureSampler lod = { }; - return _prepareShadingData(v, m, camPosW, lod); + return _prepareShadingData(v, m, camPosW, lod, true); } /** Prepare the hit-point data -The `lod` parameter represents the level of detail to use for all material -texture fetches. + The `lod` parameter represents the level of detail to use for all material + texture fetches. */ -ShadingData prepareShadingData(VertexOut v, MaterialData m, float3 camPosW, float lod) +ShadingData prepareShadingData(VertexData v, MaterialData m, float3 camPosW, float lod) { ExplicitLodTextureSampler explicitLOD = { lod }; - return _prepareShadingData(v, m, camPosW, explicitLOD); + return _prepareShadingData(v, m, camPosW, explicitLOD, true); } /** Prepare the hit-point data -The `gradX` and `gradY` parameters should be the screen-space gradients of -`v.texC` with respect to screen-space X and Y, respectively. These gradient -values will be used for all material texture fetches. + The `gradX` and `gradY` parameters should be the screen-space gradients of + `v.texC` with respect to screen-space X and Y, respectively. These gradient + values will be used for all material texture fetches. */ -ShadingData prepareShadingData(VertexOut v, MaterialData m, float3 camPosW, float2 gradX, float2 gradY) +ShadingData prepareShadingData(VertexData v, MaterialData m, float3 camPosW, float2 gradX, float2 gradY) { ExplicitGradientTextureSampler lod = { gradX, gradY }; - return _prepareShadingData(v, m, camPosW, lod); + return _prepareShadingData(v, m, camPosW, lod, true); } ShadingResult initShadingResult() @@ -357,17 +345,17 @@ ShadingResult evalMaterial(ShadingData sd, LightData light, float shadowFactor) LightSample ls = evalLight(light, sd); // If the light doesn't hit the surface or we are viewing the surface from the back, return - if(ls.NdotL <= 0) return sr; + if (ls.NdotL <= 0) return sr; sd.NdotV = saturate(sd.NdotV); // Calculate the diffuse term - sr.diffuseBrdf = saturate(evalDiffuseBrdf(sd, ls)); + sr.diffuseBrdf = evalDiffuseBrdf(sd, ls); sr.diffuse = ls.diffuse * sr.diffuseBrdf * ls.NdotL; sr.color.rgb = sr.diffuse; sr.color.a = sd.opacity; // Calculate the specular term - sr.specularBrdf = saturate(evalSpecularBrdf(sd, ls)); + sr.specularBrdf = evalSpecularBrdf(sd, ls); sr.specular = ls.specular * sr.specularBrdf * ls.NdotL; sr.color.rgb += sr.specular; @@ -388,5 +376,3 @@ ShadingResult evalMaterial(ShadingData sd, LightProbeData probe) sr.color.rgb += sr.specular; return sr; } - -#endif // _FALCOR_SHADING_SLANG_ diff --git a/Source/Falcor/ShadingUtils/Skinning.slang b/Source/Falcor/ShadingUtils/Skinning.slang new file mode 100644 index 000000000..a7c9cec6f --- /dev/null +++ b/Source/Falcor/ShadingUtils/Skinning.slang @@ -0,0 +1,149 @@ +/*************************************************************************** +# Copyright (c) 201*, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "HostDeviceData.h" + +struct SkinningData +{ + StructuredBuffer staticData; + StructuredBuffer dynamicData; + RWStructuredBuffer skinnedVertices; + Buffer boneMatrices; + Buffer inverseTransposeBoneMatrices; + Buffer worldMatrices; + Buffer inverseTransposeWorldMatrices; + + float4x4 getTransposeWorldMatrix(uint matrixID) + { + float4x4 m = float4x4(worldMatrices[matrixID * 4 + 0], + worldMatrices[matrixID * 4 + 1], + worldMatrices[matrixID * 4 + 2], + worldMatrices[matrixID * 4 + 3]); + + return transpose(m); + } + + float4x4 getInverseWorldMatrix(uint matrixID) + { + float4x4 m = float4x4(inverseTransposeWorldMatrices[matrixID * 4 + 0], + inverseTransposeWorldMatrices[matrixID * 4 + 1], + inverseTransposeWorldMatrices[matrixID * 4 + 2], + inverseTransposeWorldMatrices[matrixID * 4 + 3]); + + return transpose(m); + } + + float4x4 getBoneMatrix(uint matrixID) + { + return float4x4(boneMatrices[matrixID * 4 + 0], + boneMatrices[matrixID * 4 + 1], + boneMatrices[matrixID * 4 + 2], + boneMatrices[matrixID * 4 + 3]); + } + + float4x4 getInverseTransposeBoneMatrix(uint matrixID) + { + return float4x4(inverseTransposeBoneMatrices[matrixID * 4 + 0], + inverseTransposeBoneMatrices[matrixID * 4 + 1], + inverseTransposeBoneMatrices[matrixID * 4 + 2], + inverseTransposeBoneMatrices[matrixID * 4 + 3]); + } + + float4x4 getBlendedMatrix(uint vertexId) + { + DynamicVertexData d = dynamicData[vertexId]; + + float4x4 boneMat = getBoneMatrix(d.boneID.x) * d.boneWeight.x; + boneMat += getBoneMatrix(d.boneID.y) * d.boneWeight.y; + boneMat += getBoneMatrix(d.boneID.z) * d.boneWeight.z; + boneMat += getBoneMatrix(d.boneID.w) * d.boneWeight.w; + + boneMat = mul(boneMat, getInverseWorldMatrix(d.globalMatrixID)); + return boneMat; + } + + float4x4 getInverseTransposeBlendedMatrix(uint vertexId) + { + DynamicVertexData d = dynamicData[vertexId]; + + float4x4 boneMat = getInverseTransposeBoneMatrix(d.boneID.x) * d.boneWeight.x; + boneMat += getInverseTransposeBoneMatrix(d.boneID.y) * d.boneWeight.y; + boneMat += getInverseTransposeBoneMatrix(d.boneID.z) * d.boneWeight.z; + boneMat += getInverseTransposeBoneMatrix(d.boneID.w) * d.boneWeight.w; + + boneMat = mul(boneMat, getTransposeWorldMatrix(d.globalMatrixID)); + return boneMat; + } + + uint getStaticVertexID(uint vertexId) + { + return dynamicData[vertexId].staticIndex; + } + + StaticVertexData getStaticVertexData(uint vertexId) + { + return staticData[getStaticVertexID(vertexId)]; + } + + void storeStaticData(uint vertexId, StaticVertexData data) + { + gData.skinnedVertices[getStaticVertexID(vertexId)] = data; + } + + float3 getCurrentPosition(uint vertexId) + { + return gData.skinnedVertices[getStaticVertexID(vertexId)].position; + } +}; + +ParameterBlock gData; + +[numthreads(256, 1, 1)] +void main(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + // Check that this is an active vertex + uint vertexId = dispatchThreadID.x; + uint vertexCount, stride; + gData.dynamicData.GetDimensions(vertexCount, stride); + if (vertexId >= vertexCount) return; + + // Blend the vertices + StaticVertexData s = gData.getStaticVertexData(vertexId); + float4x4 boneMat = gData.getBlendedMatrix(vertexId); + float4x4 invTransposeMat = gData.getInverseTransposeBlendedMatrix(vertexId); + + s.prevPosition = gData.getCurrentPosition(vertexId); + s.position = mul(float4(s.position, 1.f), boneMat).xyz; + s.bitangent = mul(s.bitangent, (float3x3) boneMat); + s.normal = mul(s.normal, (float3x3) transpose(invTransposeMat)); + + // Store the result + gData.storeStaticData(vertexId, s); +} + + diff --git a/Framework/Source/UnitTest.cpp b/Source/Falcor/Testing/UnitTest.cpp similarity index 80% rename from Framework/Source/UnitTest.cpp rename to Source/Falcor/Testing/UnitTest.cpp index 3b79ab660..b0976ba36 100644 --- a/Framework/Source/UnitTest.cpp +++ b/Source/Falcor/Testing/UnitTest.cpp @@ -25,10 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" #include "UnitTest.h" - -#include "Utils/Platform/OS.h" - #include #include #include @@ -36,10 +34,8 @@ namespace Falcor { - namespace { - struct Test { std::string getTitle() const @@ -136,8 +132,8 @@ namespace Falcor { ++nFailures; fprintf(file, "%s", failureMessage.c_str()); - if (!failureDetails.empty()) fprintf(file, "%s", failureDetails.c_str()); } + if (!failureDetails.empty()) fprintf(file, "%s", failureDetails.c_str()); } return nFailures; @@ -149,44 +145,58 @@ namespace Falcor const std::string& entry, const Program::DefineList& programDefines, Shader::CompilerFlags flags, - const std::string& shaderModel) + const std::string& shaderModel, + bool createShaderVars) { + // Create program. mpProgram = ComputeProgram::createFromFile(path, entry, programDefines, flags, shaderModel); + if (!mpProgram) throw ErrorRunningTestException("Couldn't create program"); mpState = ComputeState::create(); mpState->setProgram(mpProgram); + // Create vars unless it should be deferred. + if (createShaderVars) createVars(); + } + + void GPUUnitTestContext::createVars() + { // Create shader variables. ProgramReflection::SharedConstPtr pReflection = mpProgram->getReflector(); + if (!pReflection) throw ErrorRunningTestException("Couldn't create program reflector"); mpVars = ComputeVars::create(pReflection); + if (!mpVars) throw ErrorRunningTestException("Couldn't create program vars"); - if (!mpVars) throw ErrorRunningTestException("couldn't create vars"); - - // Try to use shader reflection to query thread group size. ((1,1,1) - // is assumed if it's not specified.) + // Try to use shader reflection to query thread group size. + // ((1,1,1) is assumed if it's not specified.) mThreadGroupSize = pReflection->getThreadGroupSize(); assert(mThreadGroupSize.x >= 1 && mThreadGroupSize.y >= 1 && mThreadGroupSize.z >= 1); } - void GPUUnitTestContext::allocateStructuredBuffer(const std::string& name, size_t nElements) + void GPUUnitTestContext::allocateStructuredBuffer(const std::string& name, size_t nElements, const void* pInitData, size_t initDataSize) { - mStructuredBuffers[name].pBuffer = StructuredBuffer::create(mpProgram, name, nElements); + assert(mpVars); + mStructuredBuffers[name].pBuffer = StructuredBuffer::create(mpProgram.get(), name, nElements); if (!mStructuredBuffers[name].pBuffer) throw ErrorRunningTestException(name + ": couldn't create structured buffer"); + if (pInitData) + { + size_t expectedDataSize = mStructuredBuffers[name].pBuffer->getElementSize() * mStructuredBuffers[name].pBuffer->getElementCount(); + if (initDataSize == 0) initDataSize = expectedDataSize; + else if (initDataSize != expectedDataSize) throw ErrorRunningTestException("StructuredBuffer '" + name + "' initial data size mismatch"); + //mStructuredBuffers[name].pBuffer->updateData(pInitData, 0, initDataSize); + mStructuredBuffers[name].pBuffer->setBlob(pInitData, 0, initDataSize); + } } - template static T div_round_up(T a, T b) { return (a + b - (T)1) / b; } - - void GPUUnitTestContext::runProgram(int32_t width, int32_t height, int32_t depth) + void GPUUnitTestContext::runProgram(const glm::uvec3& dimensions) { + assert(mpVars); for (const auto& buffer : mStructuredBuffers) { mpVars->setStructuredBuffer(buffer.first, buffer.second.pBuffer); } - mpContext->pushComputeState(mpState); - mpContext->pushComputeVars(mpVars); - - uvec3 groups = div_round_up(glm::uvec3(width, height, depth), mThreadGroupSize); + uvec3 groups = div_round_up(dimensions, mThreadGroupSize); #ifdef FALCOR_D3D12 // Check dispatch dimensions. TODO: Should be moved into Falcor. @@ -198,10 +208,7 @@ namespace Falcor } #endif // FALCOR_D3D12 - mpContext->dispatch(groups.x, groups.y, groups.z); - - mpContext->popComputeVars(); - mpContext->popComputeState(); + mpContext->dispatch(mpState.get(), mpVars.get(), groups); } void GPUUnitTestContext::unmapBuffer(const char* bufferName) @@ -212,7 +219,7 @@ namespace Falcor mStructuredBuffers[bufferName].mapped = false; } - const void *GPUUnitTestContext::mapRawRead(const char* bufferName) + const void* GPUUnitTestContext::mapRawRead(const char* bufferName) { assert(mStructuredBuffers.find(bufferName) != mStructuredBuffers.end()); if (mStructuredBuffers.find(bufferName) == mStructuredBuffers.end()) @@ -224,7 +231,8 @@ namespace Falcor return mStructuredBuffers[bufferName].pBuffer->map(Buffer::MapType::Read); } - /* Simple tests of the testing framework. How meta. */ + /** Simple tests of the testing framework. How meta. + */ CPU_TEST(TestCPUTest) { EXPECT_EQ(1, 1); @@ -236,6 +244,26 @@ namespace Falcor EXPECT_GE(3, 2); } + CPU_TEST(TestSingleEval) + { + // Make sure that arguments to test macros are only evaluated once. + int i = 0; + EXPECT_EQ(++i, 1); + EXPECT_EQ(i, 1); + EXPECT_NE(++i, 3); + EXPECT_EQ(i, 2); + EXPECT_LT(++i, 4); + EXPECT_EQ(i, 3); + EXPECT_LE(++i, 4); + EXPECT_EQ(i, 4); + EXPECT_GT(++i, 4); + EXPECT_EQ(i, 5); + EXPECT_GE(++i, 6); + EXPECT_EQ(i, 6); + EXPECT(++i == 7); + EXPECT_EQ(i, 7); + } + GPU_TEST(TestGPUTest) { ctx.createProgram("UnitTest.cs.hlsl"); diff --git a/Framework/Source/UnitTest.h b/Source/Falcor/Testing/UnitTest.h similarity index 52% rename from Framework/Source/UnitTest.h rename to Source/Falcor/Testing/UnitTest.h index b94dec633..4b72eb0ea 100644 --- a/Framework/Source/UnitTest.h +++ b/Source/Falcor/Testing/UnitTest.h @@ -37,13 +37,8 @@ #include #include -/** - -This file defines both the user-visible API for the unit testing framework -as well as the various classes that implement it. For documentation about -how to use it, please see https://gitlab-master.nvidia.com/nvresearch-gfx/Tools/Falcor/wikis/testing/unit-testing. - - */ +/** This file defines both the user-visible API for the unit testing framework as well as the various classes that implement it +*/ namespace Falcor { @@ -66,16 +61,13 @@ namespace Falcor }; using CPUTestFunc = std::function; - void registerCPUTest(const std::string& filename, const std::string& name, - CPUTestFunc func); - using GPUTestFunc = std::function; - void registerGPUTest(const std::string& filename, const std::string& name, - GPUTestFunc func); - int32_t runTests(FILE *file, RenderContext* pRenderContext, const std::string& testFilterRegexp); + dlldecl void registerCPUTest(const std::string& filename, const std::string& name, CPUTestFunc func); + dlldecl void registerGPUTest(const std::string& filename, const std::string& name, GPUTestFunc func); + dlldecl int32_t runTests(FILE *file, RenderContext* pRenderContext, const std::string& testFilterRegexp); - class UnitTestContext + class dlldecl UnitTestContext { public: /** reportFailure is called with an error message to report a failing @@ -96,11 +88,11 @@ namespace Falcor std::string mFailureMessage; }; - class CPUUnitTestContext : public UnitTestContext + class dlldecl CPUUnitTestContext : public UnitTestContext { }; - class GPUUnitTestContext : public UnitTestContext + class dlldecl GPUUnitTestContext : public UnitTestContext { public: GPUUnitTestContext(RenderContext* pContext) : mpContext(pContext) { } @@ -109,23 +101,35 @@ namespace Falcor given path. The entrypoint is assumed to be |main()| unless otherwise specified with the |csEntry| parameter. Preprocessor defines and compiler flags can also be optionally provided. - */ + */ void createProgram(const std::string& path, const std::string& csEntry = "main", const Program::DefineList& programDefines = Program::DefineList(), Shader::CompilerFlags flags = Shader::CompilerFlags::None, - const std::string& shaderModel = ""); + const std::string& shaderModel = "", + bool createShaderVars = true); + + /** (Re-)create the shader variables. Call this if vars were not + created in createProgram() (if createVars = false), or after + the shader variables have changed through specialization. + */ + void createVars(); /** vars returns the ComputeVars for the program for use in binding textures, etc. - */ - ComputeVars& vars() { return *mpVars; } + */ + ComputeVars& vars() + { + assert(mpVars); + return *mpVars; + } - /** operator[] returns the |ConstantBuffer| with the given name (or - nullptr if no such constant buffer exists). - */ + /** operator[] returns the |ConstantBuffer| with the given name + (or nullptr if no such constant buffer exists, in which case an error is logged). + */ ConstantBuffer::SharedPtr operator[](const std::string& cbName) { + assert(mpVars); return mpVars->getDefaultBlock()->getConstantBuffer(cbName); } @@ -136,14 +140,26 @@ namespace Falcor TODO: support structured buffers in parameter blocks? TODO: add support for other buffer allocation types? + + \param[in] name Name of the buffer in the shader. + \param[in] nElements Number of elements to allocate. + \param[in] pInitData Optional parameter. Initial buffer data. + \param[in] initDataSize Optional parameter. Size of the pointed initial data for validation (if 0 the buffer is assumed to be of the right size). */ - void allocateStructuredBuffer(const std::string& name, size_t nElements); + void allocateStructuredBuffer(const std::string& name, size_t nElements, const void* pInitData = nullptr, size_t initDataSize = 0); - /* runProgram runs the compute program that was specified in - |createProgram|, where the total number of threads that runs is - given by the product of the three provided dimensions. - */ - void runProgram(int32_t width = 1, int32_t height = 1, int32_t depth = 1); + /** runProgram runs the compute program that was specified in + |createProgram|, where the total number of threads that runs is + given by the product of the three provided dimensions. + \param[in] dimensions Number of threads to dispatch in each dimension. + */ + void runProgram(const glm::uvec3& dimensions); + + /** runProgram runs the compute program that was specified in + |createProgram|, where the total number of threads that runs is + given by the product of the three provided dimensions. + */ + void runProgram(uint32_t width = 1, uint32_t height = 1, uint32_t depth = 1) { runProgram(glm::uvec3(width, height, depth)); } /** mapBuffer returns a pointer to the named structured buffer. Returns nullptr if no such buffer exists. SFINAE is used to @@ -160,20 +176,30 @@ namespace Falcor */ void unmapBuffer(const char* bufferName); + /** Returns the current Falcor render context. + */ + RenderContext* getRenderContext() const { return mpContext; } + + /** Returns the program. + */ + ComputeProgram* getProgram() const { return mpProgram.get(); } + private: - RenderContext * mpContext; + const void* mapRawRead(const char* bufferName); + + // Internal state + RenderContext* mpContext; ComputeState::SharedPtr mpState; ComputeProgram::SharedPtr mpProgram; ComputeVars::SharedPtr mpVars; glm::uvec3 mThreadGroupSize = { 0, 0, 0 }; + struct ParameterBuffer { StructuredBuffer::SharedPtr pBuffer; bool mapped = false; }; std::map mStructuredBuffers; - - const void* mapRawRead(const char* bufferName); }; /** StreamSink is a utility class used by the testing framework that either @@ -182,13 +208,21 @@ namespace Falcor StreamSink does the former, and if it has passed, it does the latter.) In the event of a test failure, passes along the failure message to the provided GPUUnitTestContext's |reportFailure| method. - */ + */ class StreamSink { public: - /* If a non-nullptr UnitTestContext is provided, the values printed - will be accumulated and passed to the context's reportFailure() - method when the StreamSink destructor runs. */ + /** We need to declare this constructor in order to return + StreamSinks as rvalues from functions because we've declared a + StreamSink destructor below. + */ + StreamSink(StreamSink &&) = default; + + /** Construct a StreamSink for a test context. + If a non-nullptr UnitTestContext is provided, the values printed + will be accumulated and passed to the context's reportFailure() + method when the StreamSink destructor runs. + */ StreamSink(UnitTestContext* ctx) : mpCtx(ctx) {} ~StreamSink() @@ -205,9 +239,99 @@ namespace Falcor private: std::stringstream mSs; - UnitTestContext* mpCtx; + UnitTestContext* mpCtx = nullptr; }; + template + inline StreamSink expectEqInternal(T x, const char* xString, U y, const char* yString, + UnitTestContext& ctx, const char* file, int line) { + if (x == y) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString << " == " << + yString << " (" << x << " vs. " << y << ") "; + return ss; + } + + template + inline StreamSink expectNeInternal(T x, const char* xString, U y, const char* yString, + UnitTestContext& ctx, const char* file, int line) { + if (x != y) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString << " != " << + yString << " (" << x << " vs. " << y << ") "; + return ss; + } + + template + inline StreamSink expectGeInternal(T x, const char* xString, U y, const char* yString, + UnitTestContext& ctx, const char* file, int line) { + if (x >= y) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString << " >= " << + yString << " (" << x << " vs. " << y << ") "; + return ss; + } + + template + inline StreamSink expectGtInternal(T x, const char* xString, U y, const char* yString, + UnitTestContext& ctx, const char* file, int line) { + if (x > y) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString << " > " << + yString << " (" << x << " vs. " << y << ") "; + return ss; + } + + template + inline StreamSink expectLeInternal(T x, const char* xString, U y, const char* yString, + UnitTestContext& ctx, const char* file, int line) { + if (x <= y) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString << " <= " << + yString << " (" << x << " vs. " << y << ") "; + return ss; + } + + template + inline StreamSink expectLtInternal(T x, const char* xString, U y, const char* yString, + UnitTestContext& ctx, const char* file, int line) { + if (x < y) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString << " < " << + yString << " (" << x << " vs. " << y << ") "; + return ss; + } + + template + inline StreamSink expectInternal(T x, const char* xString, UnitTestContext& ctx, + const char* file, int line) { + if (x) return StreamSink(nullptr); + + if (++ctx.mNumFailures == kMaxTestFailures) throw TooManyFailedTestsException(); + + StreamSink ss(&ctx); + ss << file << ":" << line << " Test failed: " << xString; + return ss; + } + /////////////////////////////////////////////////////////////////////////// /** Start of user-facing API */ @@ -246,73 +370,18 @@ namespace Falcor if (foo) // look, no braces EXPECT_EQ(x, y); - It is, however, a little tricky to make that work: we'd like to all - EXPECT*s be single statements, but that's tricky since we'd also like - to throw an exception and abort the test if too many EXPECT*s fail. - One might think to do that from the StreamSink destructor, since that's - a natural place to check how many failures we've seen, but... it's - illegal to throw exceptions from destructors. - - Therefore, we take advantage of the comma operator for the throw in - that case. In the event of a failed test, we see if there have been - too many errors and throw if appropriate. But... there's one more - thing: we can't directly use it in C++'s ternary operator since not - only is throw a statement, but the types of the true and false sides - have to be the same. However, function call is an expression, and - it's legit to have two void function calls in a ternary expression. - - Thus, we have two helper functions that throw or not, call the - appropriate one based on the EXPECT* test and the number of failed - tests, then use the comma operator to move on to the declaration of a - StreamSink variable to take care of the test message output. - - For maximum C++ grossness, I suppose these could have been lambdas. - + The work of the test and accounting for failures is taken care of by various + expect*Internal() functions; this ensures that the macro operands are only + evaluated once and that we can do non-trivial work in a function without + getting into contortions. */ - inline void throwTooManyFailures() { throw TooManyFailedTestsException(); } - inline void dontThrow() { } - - /** Note: the tests are written as they are so that NaNs are handled correctly. - */ -#define EXPECT_EQ(x, y) \ - (((!((x) == (y)) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!((x) == (y)) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " == " #y " (" << \ - x << " vs. " << y << ") " - -#define EXPECT_GE(x, y) \ - (((!((x) >= (y)) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!((x) >= (y)) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " >= " #y " (" << \ - x << " vs. " << y << ") " - -#define EXPECT_GT(x, y) \ - (((!((x) > (y)) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!((x) > (y)) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " > " #y " (" << \ - x << " vs. " << y << ") " - -#define EXPECT_LE(x, y) \ - (((!((x) <= (y)) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!((x) <= (y)) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " <= " #y " (" << \ - x << " vs. " << y << ") " - -#define EXPECT_LT(x, y) \ - (((!((x) < (y)) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!((x) < (y)) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " < " #y " (" << \ - x << " vs. " << y << ") " - -#define EXPECT_NE(x, y) \ - (((!((x) != (y)) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!((x) != (y)) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " != " #y " (" << \ - x << " vs. " << y << ") " - -#define EXPECT(x) \ - (((!(x) && ++ctx.mNumFailures == kMaxTestFailures) ? throwTooManyFailures() : dontThrow()), \ - StreamSink(!(x) ? &ctx : nullptr)) << \ - __FILE__ ":" << __LINE__ << " Test failed: " #x " " + +#define EXPECT_EQ(x, y) expectEqInternal((x), #x, (y), #y, ctx, __FILE__, __LINE__) +#define EXPECT_NE(x, y) expectNeInternal((x), #x, (y), #y, ctx, __FILE__, __LINE__) +#define EXPECT_GE(x, y) expectGeInternal((x), #x, (y), #y, ctx, __FILE__, __LINE__) +#define EXPECT_GT(x, y) expectGtInternal((x), #x, (y), #y, ctx, __FILE__, __LINE__) +#define EXPECT_LE(x, y) expectLeInternal((x), #x, (y), #y, ctx, __FILE__, __LINE__) +#define EXPECT_LT(x, y) expectLtInternal((x), #x, (y), #y, ctx, __FILE__, __LINE__) +#define EXPECT(x) expectInternal((x), #x, ctx, __FILE__, __LINE__) } // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp new file mode 100644 index 000000000..7219cce96 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "BitonicSort.h" + +#ifdef _ENABLE_NVAPI +namespace Falcor +{ + static const char kShaderFilename[] = "Utils/Algorithm/BitonicSort.cs.slang"; + + BitonicSort::SharedPtr BitonicSort::create() + { + SharedPtr ptr = SharedPtr(new BitonicSort()); + return ptr->init() ? ptr : nullptr; + } + + bool BitonicSort::execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t totalSize, uint32_t chunkSize, uint32_t groupSize) + { + PROFILE("BitonicSort::execute"); + + // Validate inputs. + assert(pRenderContext); + assert(pData); + assert(chunkSize >= 1 && chunkSize <= groupSize && isPowerOf2(chunkSize)); + assert(groupSize >= 1 && groupSize <= 1024 && isPowerOf2(groupSize)); + + // Early out if there is nothing to be done. + if (totalSize == 0 || chunkSize <= 1) return true; + + // Configure the shader for the specified chunk size. + // This will trigger a re-compile if a new chunk size is encountered. + mSort.pProgram->addDefine("CHUNK_SIZE", std::to_string(chunkSize)); + mSort.pProgram->addDefine("GROUP_SIZE", std::to_string(groupSize)); + + // Determine dispatch dimensions. + const uint32_t numGroups = div_round_up(totalSize, groupSize); + const uint32_t groupsX = std::max((uint32_t)sqrt(numGroups), 1u); + const uint32_t groupsY = div_round_up(numGroups, groupsX); + assert(groupsX * groupsY * groupSize >= totalSize); + + // Constants. The buffer size as a runtime constant as it may be variable and we don't want to recompile each time it changes. + mSort.pVars["CB"]["gTotalSize"] = totalSize; + mSort.pVars["CB"]["gDispatchX"] = groupsX; + + // Bind the data. + bool success = mSort.pVars->setRawBuffer("gData", pData); + assert(success); + + // Execute. + pRenderContext->dispatch(mSort.pState.get(), mSort.pVars.get(), {groupsX, groupsY, 1}); + + return true; + } + + bool BitonicSort::init() + { +#if !(_ENABLE_NVAPI == true) + logError("BitonicSort::init() - NVAPI is required. Set _ENABLE_NVAPI to true in FalcorConfig.h."); + return false; +#endif + mSort.pState = ComputeState::create(); + + // Create shaders + Program::DefineList defines; + defines.add("CHUNK_SIZE", "256"); // Dummy values just so we can get reflection data. We'll set the actual values in execute(). + defines.add("GROUP_SIZE", "256"); + if (!(mSort.pProgram = ComputeProgram::createFromFile(kShaderFilename, "main", defines))) + { + return false; + } + mSort.pState->setProgram(mSort.pProgram); + mSort.pVars = ComputeVars::create(mSort.pProgram.get()); + + return true; + } +} +#endif diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang b/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang new file mode 100644 index 000000000..162e62d1b --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.cs.slang @@ -0,0 +1,170 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** In-place bitonic sort of 32-bit values in chunks of N elements. + + The host sets these two defines: + CHUNK_SIZE (= N) chunk size which must be a power-of-two, and + GROUP_SIZE thread group size, must be a power-of-two <= 1024. + + The code uses horizontal instructions to shuffle within the warp when possible, + and shared memory to shuffle between warps. + Shuffles are not yet available in shader model 6.0+, we therefore rely on NVAPI. +*/ + +// Setup NvApi. We need this to get shuffle-xor operations. +#define NV_SHADER_EXTN_SLOT u63 +#define NV_SHADER_EXTN_REGISTER_SPACE space0 +#include "NVAPI/nvHLSLExtns.h" + +#if (NV_WARP_SIZE != 32) +#error Kernel assumes warp size 32 +#endif + +// Check constraints. +// The kernel is currently written for a 1:1 mapping between elements to sort and threads. +#if (CHUNK_SIZE > GROUP_SIZE) +#error CHUNK_SIZE > GROUP_SIZE not supported +#endif + +cbuffer CB +{ + uint gTotalSize; ///< Total number of elements. + uint gDispatchX; ///< Number of thread groups in dispatch dimension X. +}; + +RWByteAddressBuffer gData; ///< The data buffer we're sorting in-place. + +groupshared uint gSharedData[GROUP_SIZE * 2]; ///< Temporary working buffer in shared memory. + + +/** Within warp bitonic sort, for iterations {j, j/2, ..., 1}, where j <= 16. + \param[in,out] value The current thread's value. + \param[in] i Global element index. + \param[in] j Start element offset j<=16. + \param[in] dir Sorting ascending (true) or descending (false). +*/ +void bitonicSortInWarp(inout uint value, uint i, uint j, bool dir) +{ + [unroll] + for (; j > 0; j >>= 1) + { + // Get index of sorting partner in chunk + uint value_ixj = NvShflXor(value, j); // Value from current lane ^ j + + // Decide whether to swap. + bool pred = (((i & j) == 0) != dir) == value < value_ixj; + if (pred) value = value_ixj; + } +} + +/** In-place bitonic sort. +*/ +[numthreads(GROUP_SIZE, 1, 1)] +void main(uint3 groupID : SV_GroupID, uint groupIdx : SV_GroupIndex) +{ + const uint group = groupID.y * gDispatchX + groupID.x; // Sequential group index. + const uint thid = groupIdx; // Local thread index in group (range 0..GROUP_SIZE-1). + + const uint globalIdx = group * GROUP_SIZE + thid; // Global element index in gData + const uint globalAddr = globalIdx * 4; // Address of current element in gData + + const uint N = CHUNK_SIZE; // Number of elements per chunk to sort. Must be a power-of-two. + const uint i = globalIdx & (N - 1); // i = local index of element in chunk, range [0,N). + + // Load value from memory. + // Out-of-bounds elements are set to UINT_MAX (-1) to be placed last and allow data that is not a multiple of chunk size. + uint value = uint(-1); + if (globalIdx < gTotalSize) + { + value = gData.Load(globalAddr); + } + + // Major steps for k = {2,4,...,32} are done within warp. + [unroll] + for (uint k = 2; k <= min(N,32); k <<= 1) + { + // Minor steps for iterations j = {16, 8, ..., 1} in warp. + const bool dir = ((i & k) == 0); // Sort ascending (true) or descending (false) + uint j = k >> 1; // j <= 16 + bitonicSortInWarp(value, i, j, dir); + } + +#if (CHUNK_SIZE > 32) + // Load data into shared memory. + gSharedData[thid * 2] = value; + GroupMemoryBarrierWithGroupSync(); + + // Major steps for k = {64,128,...N} are done in shared memory. + [unroll] + for (uint k = 64; k <= N; k <<= 1) + { + const bool dir = ((i & k) == 0); // Sort ascending (true) or descending (false) + + // We ping-pong data in shared memory between adjacent addresses, using offset = {0, 1} to denote which one. + uint offset = 0; + + // Minor steps for iterations j = {k/2, k/4, ..., 32} in shared memory. + [unroll] + for (uint j = k >> 1; j >= 32; j >>= 1) + { + // Get sorting partner. + uint value_ixj = gSharedData[(thid ^ j) * 2 + offset]; + + // Decide whether to swap. See comments in bitonicSortInWarp(). + bool pred = (((i & j) == 0) != dir) == value < value_ixj; + if (pred) value = value_ixj; + + // Store result for next minor step (except for last iteration). Write to offset address to avoid race condition. + if (j > 32) + { + gSharedData[thid * 2 + (offset ^ 1)] = value; + GroupMemoryBarrierWithGroupSync(); + offset ^= 1; + } + } + + // Minor steps for iterations j = {16, 8, ..., 1} in warp. + uint jStart = min(k >> 1, 16); + bitonicSortInWarp(value, i, jStart, dir); + + // Store result for major step (except for last iteration). + if (k < N) + { + gSharedData[thid * 2] = value; + GroupMemoryBarrierWithGroupSync(); + } + } +#endif + + // Write result to memory. + if (globalIdx < gTotalSize) + { + gData.Store(globalAddr, value); + } +} diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.h b/Source/Falcor/Utils/Algorithm/BitonicSort.h new file mode 100644 index 000000000..6d87f5e9c --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.h @@ -0,0 +1,75 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/State/ComputeState.h" +#include "Core/Program/ComputeProgram.h" +#include "Core/Program/ProgramVars.h" + +namespace Falcor +{ +#ifdef _ENABLE_NVAPI + /** In-place bitonic sort in chunks of N elements. + + This sort method is efficient for sorting shorter sequences. + The time complexity is O(N*log^2(N)), but it parallelizes very well and has practically no branching. + The sort is implemented using horizontal operations within warps, and shared memory across warps. + + This code requires an NVIDIA GPU and NVAPI. Set _ENABLE_NVAPI to true in FalcorConfig.h. + */ + class dlldecl BitonicSort : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; + virtual ~BitonicSort() = default; + + static SharedPtr create(); + + /** In-place bitonic sort in chunks of N elements. Each chunk is sorted in ascending order. + \param[in] pRenderContext The render context. + \param[in] pData The data buffer to sort in-place. + \param[in] totalSize The total number of elements in the buffer. This does _not_ have to be a multiple of chunkSize. + \param[in] chunkSize The number of elements per chunk. Each chunk is individually sorted. Must be a power-of-two in the range [1, groupSize]. + \param[in] groupSize Thread group size. Must be a power-of-two in the range [1,1024]. The default group size of 256 is generally the fastest. + \return True if successful, false if an error occured. + */ + bool execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t totalSize, uint32_t chunkSize, uint32_t groupSize = 256); + + protected: + BitonicSort() = default; + bool init(); + + struct + { + ComputeState::SharedPtr pState; + ComputeProgram::SharedPtr pProgram; + ComputeVars::SharedPtr pVars; + } mSort; + }; +#endif +} diff --git a/Source/Falcor/Utils/Algorithm/ComputeParallelReduction.cpp b/Source/Falcor/Utils/Algorithm/ComputeParallelReduction.cpp new file mode 100644 index 000000000..811715b3b --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/ComputeParallelReduction.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ComputeParallelReduction.h" +#include "ParallelReductionType.h" + +namespace Falcor +{ + static const char kShaderFile[] = "Utils/Algorithm/ParallelReduction.cs.slang"; + static const char kShaderModel[] = "6_0"; + + ComputeParallelReduction::SharedPtr ComputeParallelReduction::create() + { + SharedPtr ptr = SharedPtr(new ComputeParallelReduction()); + return ptr->init() ? ptr : nullptr; + } + + bool ComputeParallelReduction::init() + { + // Create the programs. + // Set defines to avoid compiler warnings about undefined macros. Proper values will be assigned at runtime. + Program::DefineList defines = { { "FORMAT_CHANNELS", "1" }, { "FORMAT_TYPE", "1" } }; + if (!(mpInitialProgram = ComputeProgram::createFromFile(kShaderFile, "initialPass", defines, Shader::CompilerFlags::None, kShaderModel))) return false; + if (!(mpFinalProgram = ComputeProgram::createFromFile(kShaderFile, "finalPass", defines, Shader::CompilerFlags::None, kShaderModel))) return false; + if (!(mpVars = ComputeVars::create(mpInitialProgram.get()))) return false; + + // Check assumptions on thread group sizes. The initial pass is a 2D dispatch, the final pass a 1D. + assert(mpInitialProgram->getReflector()->getThreadGroupSize().z == 1); + assert(mpFinalProgram->getReflector()->getThreadGroupSize().y == 1 && mpFinalProgram->getReflector()->getThreadGroupSize().z == 1); + + mpState = ComputeState::create(); + + return true; + } + + bool ComputeParallelReduction::allocate(uint32_t elementCount) + { + if (mpBuffers[0] == nullptr || mpBuffers[0]->getElementCount() < elementCount) + { + // Buffer 0 has one element per tile. + mpBuffers[0] = TypedBuffer::create(elementCount); + if (!mpBuffers[0]) return false; + + // Buffer 1 has one element per N elements in buffer 0. + const uint32_t numElem1 = div_round_up(elementCount, mpFinalProgram->getReflector()->getThreadGroupSize().x); + if (mpBuffers[1] == nullptr || mpBuffers[1]->getElementCount() < numElem1) + { + mpBuffers[1] = TypedBuffer::create(numElem1); + if (!mpBuffers[1]) return false; + } + } + return true; + } + + template + bool ComputeParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, T* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset) + { + PROFILE("ComputeParallelReduction::execute"); + + // Check texture array/mip/sample count. + if (pInput->getArraySize() != 1 || pInput->getMipCount() != 1 || pInput->getSampleCount() != 1) + { + logError("ComputeParallelReduction::execute() - Input texture is unsupported. Aborting."); + return false; + } + + // Check texture format. + uint32_t formatType = FORMAT_TYPE_UNKNOWN; + switch (getFormatType(pInput->getFormat())) + { + case FormatType::Float: + case FormatType::Unorm: + case FormatType::Snorm: + formatType = FORMAT_TYPE_FLOAT; + break; + case FormatType::Sint: + formatType = FORMAT_TYPE_SINT; + break; + case FormatType::Uint: + formatType = FORMAT_TYPE_UINT; + break; + default: + logError("ComputeParallelReduction::execute() - Input texture format unsupported. Aborting."); + return false; + } + + // Check that reduction type T is compatible with the resource format. + if (sizeof(T::value_type) != 4 || // The shader is written for 32-bit types + (formatType == FORMAT_TYPE_FLOAT && !std::is_floating_point::value) || + (formatType == FORMAT_TYPE_SINT && (!std::is_integral::value || !std::is_signed::value)) || + (formatType == FORMAT_TYPE_UINT && (!std::is_integral::value || !std::is_unsigned::value))) + { + logError("ComputeParallelReduction::execute() - Template type T is not compatible with resource format. Aborting."); + return false; + } + + // Allocate intermediate buffers if needed. + const glm::uvec2 resolution = glm::uvec2(pInput->getWidth(), pInput->getHeight()); + assert(resolution.x > 0 && resolution.y > 0); + + const glm::uvec2 numTiles = div_round_up(resolution, glm::uvec2(mpInitialProgram->getReflector()->getThreadGroupSize())); + if (!allocate(numTiles.x * numTiles.y)) + { + logError("ComputeParallelReduction::execute() - Failed to allocate intermediate buffers. Aborting."); + return false; + } + + assert(mpBuffers[0]); + assert(mpBuffers[1]); + + // Configure program. + const uint32_t channelCount = getFormatChannelCount(pInput->getFormat()); + assert(channelCount >= 1 && channelCount <= 4); + mpInitialProgram->addDefine("FORMAT_CHANNELS", std::to_string(channelCount)); + mpFinalProgram->addDefine("FORMAT_CHANNELS", std::to_string(channelCount)); + + mpInitialProgram->addDefine("FORMAT_TYPE", std::to_string(formatType)); + mpFinalProgram->addDefine("FORMAT_TYPE", std::to_string(formatType)); + + // Initial pass: Reduction over tiles of pixels in input texture. + mpVars["PerFrameCB"]["gResolution"] = resolution; + mpVars["PerFrameCB"]["gNumTiles"] = numTiles; + mpVars["gInput"] = pInput; + mpVars->setTypedBuffer("gResult", mpBuffers[0]); + + mpState->setProgram(mpInitialProgram); + glm::uvec3 numGroups = div_round_up(glm::uvec3(resolution.x, resolution.y, 1), mpInitialProgram->getReflector()->getThreadGroupSize()); + pRenderContext->dispatch(mpState.get(), mpVars.get(), numGroups); + + // Final pass(es): Reduction by a factor N for each pass. + uint elems = numTiles.x * numTiles.y; + uint inputsBufferIndex = 0; + + while (elems > 1) + { + mpVars["PerFrameCB"]["gElems"] = elems; + mpVars->setTypedBuffer("gInputBuffer", mpBuffers[inputsBufferIndex]); + mpVars->setTypedBuffer("gResult", mpBuffers[1 - inputsBufferIndex]); + + mpState->setProgram(mpFinalProgram); + uint32_t numGroups = div_round_up(elems, mpFinalProgram->getReflector()->getThreadGroupSize().x); + pRenderContext->dispatch(mpState.get(), mpVars.get(), { numGroups, 1, 1 }); + + inputsBufferIndex = 1 - inputsBufferIndex; + elems = numGroups; + } + + // Copy the result to GPU buffer. + if (pResultBuffer) + { + if (resultOffset + 16 > pResultBuffer->getSize()) + { + logError("ComputeParallelReduction::execute() - Results buffer is too small. Aborting."); + return false; + } + + pRenderContext->copyBufferRegion(pResultBuffer.get(), resultOffset, mpBuffers[inputsBufferIndex].get(), 0, 16); + } + + // Read back the result to the CPU. + if (pResult) + { + const T* pBuf = static_cast(mpBuffers[inputsBufferIndex]->map(Buffer::MapType::Read)); + assert(pBuf); + *pResult = *pBuf; + mpBuffers[inputsBufferIndex]->unmap(); + } + + return true; + } + + // Explicit template instantiation of the supported types. + template dlldecl bool ComputeParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, glm::vec4* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset); + template dlldecl bool ComputeParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, glm::ivec4* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset); + template dlldecl bool ComputeParallelReduction::execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, glm::uvec4* pResult, Buffer::SharedPtr pResultBuffer, uint64_t resultOffset); +} diff --git a/Source/Falcor/Utils/Algorithm/ComputeParallelReduction.h b/Source/Falcor/Utils/Algorithm/ComputeParallelReduction.h new file mode 100644 index 000000000..9a4808972 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/ComputeParallelReduction.h @@ -0,0 +1,94 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/Program/ComputeProgram.h" +#include "Core/Program/ProgramVars.h" +#include "Core/State/ComputeState.h" +#include "Utils/Math/Vector.h" + +namespace Falcor +{ + /** Class that performs parallel reduction over all pixels in a texture. + + The reduction is done on recursively on blocks of n = 1024 elements. + The total number of iterations is ceil(log2(N)/10), where N is the + total number of elements (pixels). + + The numerical error for the summation operation lies between pairwise + summation (blocks of size n = 2) and naive running summation. + */ + class dlldecl ComputeParallelReduction : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; + virtual ~ComputeParallelReduction() = default; + + enum class Type + { + Sum, + }; + + /** Create parallel reduction helper. + \return Created object or nullptr if an error occured. + */ + static SharedPtr create(); + + /** Perform parallel reduction. + The computations are performed in type T, which must be compatible with the texture format: + - glm::vec4 for floating-point texture formats (float, snorm, unorm). + - glm::uvec4 for unsigned integer texture formats. + - glm::ivec4 for signed integer texture formats. + Note that unused components are set to zero if texture format has < 4 components. + For performance reasons, it is advisable to store the result in a buffer on the GPU, + and then issue an asynchronous readback in user code to avoid a full GPU flush. + + \param[in] pRenderContext The render context. + \param[in] pInput Input texture. + \param[in] operation Reduction operation. + \param[out] pResult (Optional) The result of the reduction operation is stored here if non-nullptr. Note that this requires a GPU flush! + \param[out] pResultBuffer (Optional) Buffer on the GPU to which the result is copied (16B). + \param[out] resultOffset (Optional) Byte offset into pResultBuffer to where the result should be stored. + \return True if successful, false if an error occured. + */ + template + bool execute(RenderContext* pRenderContext, const Texture::SharedPtr& pInput, Type operation, T* pResult = nullptr, Buffer::SharedPtr pResultBuffer = nullptr, uint64_t resultOffset = 0); + + private: + ComputeParallelReduction() = default; + bool init(); + bool allocate(uint32_t elementCount); + + ComputeState::SharedPtr mpState; + ComputeProgram::SharedPtr mpInitialProgram; + ComputeProgram::SharedPtr mpFinalProgram; + ComputeVars::SharedPtr mpVars; + + TypedBuffer::SharedPtr mpBuffers[2]; ///< Intermediate buffers for reduction iterations. + }; +} diff --git a/Framework/Source/Utils/DirectedGraph.h b/Source/Falcor/Utils/Algorithm/DirectedGraph.h similarity index 99% rename from Framework/Source/Utils/DirectedGraph.h rename to Source/Falcor/Utils/Algorithm/DirectedGraph.h index 19274ae12..bd7bfe59e 100644 --- a/Framework/Source/Utils/DirectedGraph.h +++ b/Source/Falcor/Utils/Algorithm/DirectedGraph.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,7 +27,6 @@ ***************************************************************************/ #pragma once #include -#include namespace Falcor { @@ -221,4 +220,4 @@ namespace Falcor should_not_get_here(); } }; -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/DirectedGraphTraversal.h b/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h similarity index 99% rename from Framework/Source/Utils/DirectedGraphTraversal.h rename to Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h index 84a361bb2..1ca97d0da 100644 --- a/Framework/Source/Utils/DirectedGraphTraversal.h +++ b/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,7 +27,7 @@ ***************************************************************************/ #pragma once #include -#include +#include #include "DirectedGraph.h" namespace Falcor @@ -231,4 +231,4 @@ namespace Falcor return hasPath(pGraph, root, root); } }; -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Math/ParallelReduction.cpp b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp similarity index 74% rename from Framework/Source/Utils/Math/ParallelReduction.cpp rename to Source/Falcor/Utils/Algorithm/ParallelReduction.cpp index 214b4be6b..ae5d55398 100644 --- a/Framework/Source/Utils/Math/ParallelReduction.cpp +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,15 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "ParallelReduction.h" -#include "Graphics/FboHelper.h" -#include "API/RenderContext.h" -#include +#include "Core/API/RenderContext.h" namespace Falcor { - const char* fsFilename = "Framework/Shaders/ParallelReduction.ps.slang"; + const char* psFilename = "Framework/Shaders/ParallelReduction.ps.slang"; ParallelReduction::ParallelReduction(ParallelReduction::Type reductionType, uint32_t readbackLatency, uint32_t width, uint32_t height, uint32_t sampleCount) : mReductionType(reductionType) { @@ -61,12 +59,11 @@ namespace Falcor { Fbo::Desc fboDesc; fboDesc.setColorTarget(0, texFormat); - res.pFbo = FboHelper::create2D(1, 1, fboDesc); + res.pFbo = Fbo::create2D(1, 1, fboDesc); } - mpFirstIterProg = FullScreenPass::create(fsFilename, defines); - mpFirstIterProg->getProgram()->addDefine("_FIRST_ITERATION"); - mpRestIterProg = FullScreenPass::create(fsFilename, defines); - mpVars = GraphicsVars::create(mpFirstIterProg->getProgram()->getReflector()); + mpFirstIterProg = FullScreenPass::create(psFilename, defines); + mpFirstIterProg->addDefine("_FIRST_ITERATION"); + mpRestIterProg = FullScreenPass::create(psFilename, defines); // Calculate the number of reduction passes if(width > kTileSize || height > kTileSize) @@ -81,7 +78,7 @@ namespace Falcor Fbo::Desc fboDesc; fboDesc.setColorTarget(0, texFormat); - mpTmpResultFbo.push_back(FboHelper::create2D(width, height, fboDesc)); + mpTmpResultFbo.push_back(Fbo::create2D(width, height, fboDesc)); } } } @@ -91,37 +88,25 @@ namespace Falcor return ParallelReduction::UniquePtr(new ParallelReduction(reductionType, readbackLatency, width, height, sampleCount)); } - void runProgram(RenderContext* pRenderCtx, Texture::SharedPtr pInput, const FullScreenPass* pProgram, Fbo::SharedPtr pDst, GraphicsVars::SharedPtr pVars, Sampler::SharedPtr pPointSampler) + void runProgram(RenderContext* pRenderCtx, Texture::SharedPtr pInput, const FullScreenPass::SharedPtr& pPass, Fbo::SharedPtr pDst, Sampler::SharedPtr pPointSampler) { - GraphicsState::SharedPtr pState = pRenderCtx->getGraphicsState(); - auto pDefaultBlock = pVars->getDefaultBlock().get(); - pDefaultBlock->setTexture("gInputTex", pInput); - pDefaultBlock->setSampler("gSampler", pPointSampler); - - //Set draw params - pState->pushFbo(pDst); - pRenderCtx->pushGraphicsVars(pVars); - - // Launch the program - pProgram->execute(pRenderCtx); - - // Restore state - pState->popFbo(); - pRenderCtx->popGraphicsVars(); - } + pPass["gInputTex"] = pInput; + pPass["gSampler"] = pPointSampler; + pPass->execute(pRenderCtx, pDst); + } glm::vec4 ParallelReduction::reduce(RenderContext* pRenderCtx, Texture::SharedPtr pInput) { - const FullScreenPass* pProgram = mpFirstIterProg.get(); + FullScreenPass::SharedPtr pPass = mpFirstIterProg; for(size_t i = 0; i < mpTmpResultFbo.size(); i++) { - runProgram(pRenderCtx, pInput, pProgram, mpTmpResultFbo[i], mpVars, mpPointSampler); - pProgram = mpRestIterProg.get(); + runProgram(pRenderCtx, pInput, pPass, mpTmpResultFbo[i], mpPointSampler); + pPass = mpRestIterProg; pInput = mpTmpResultFbo[i]->getColorTexture(0); } - runProgram(pRenderCtx, pInput, pProgram, mResultData[mCurFbo].pFbo, mpVars, mpPointSampler); + runProgram(pRenderCtx, pInput, pPass, mResultData[mCurFbo].pFbo, mpPointSampler); mResultData[mCurFbo].pReadTask = pRenderCtx->asyncReadTextureSubresource(mResultData[mCurFbo].pFbo->getColorTexture(0).get(), 0); // Read back the results mCurFbo = (mCurFbo + 1) % mResultData.size(); @@ -142,4 +127,4 @@ namespace Falcor } return result; } -} \ No newline at end of file +} diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang b/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang new file mode 100644 index 000000000..75f56a101 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.cs.slang @@ -0,0 +1,112 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Parallel reduction using shared memory and warp instructions. + + The host sets these defines: + - FORMAT_CHANNELS Number of components in the data (N=1..4). + - FORMAT_TYPE Texture format type. See ParallelReductionType.h. +*/ +#include "ParallelReductionType.h" + +cbuffer PerFrameCB +{ + uint2 gResolution; // Pixel dimensions of input texture. + uint2 gNumTiles; // Number of tiles in input texture. + uint gElems; // Number of elements in input intermediate buffer. +}; + +// Typedef the data format as 'DataType'. The format is specified from the host. +#if FORMAT_TYPE == FORMAT_TYPE_FLOAT +typedef vector DataType; +#elif FORMAT_TYPE == FORMAT_TYPE_SINT +typedef vector DataType; +#elif FORMAT_TYPE == FORMAT_TYPE_UINT +typedef vector DataType; +#endif + +// Declare the input texture. +Texture2D gInput; + +Buffer gInputBuffer; +RWBuffer gResult; + +groupshared DataType gIntermediateCache[32 /* = 1024 / 32 */]; + +DataType loadTexture(uint2 pixelCoords) +{ + DataType value = gInput[pixelCoords]; +#if FORMAT_CHANNELS < 4 + // The default value for missing components is (0,0,0,1). Reset last component to zero for consistency. + value.w = 0.f; +#endif + return value; +} + +/** Performs reduction within a thread group and writes single result to the results buffer at 'dstIdx'. +*/ +void reduce(DataType value, uint dstIdx, uint groupThreadIdx) +{ + // Add all elements within warp. Store result to shared memory. + { + value = WaveActiveSum(value); + if (WaveIsFirstLane() == 0) gIntermediateCache[groupThreadIdx / 32] = value; + } + GroupMemoryBarrierWithGroupSync(); + + // Add all elements produced by the warps. + if (groupThreadIdx < 32) + { + value = gIntermediateCache[groupThreadIdx]; + value = WaveActiveSum(value); + if (groupThreadIdx == 0) gResult[dstIdx] = value; + } +} + +[numthreads(32, 32, 1)] +void initialPass(uint3 globalThreadId : SV_DispatchThreadID, uint groupThreadIdx : SV_GroupIndex, uint3 groupId : SV_GroupID) +{ + const uint2 pixelCoords = globalThreadId.xy; + const uint tileIdx = groupId.y * gNumTiles.x + groupId.x; + + // Load input from texture in tiles of 32x32 pixels. + DataType value = 0; + if (all(pixelCoords < gResolution)) value = loadTexture(pixelCoords); + + reduce(value, tileIdx, groupThreadIdx); +} + +[numthreads(1024, 1, 1)] +void finalPass(uint3 globalThreadId : SV_DispatchThreadID, uint groupThreadIdx : SV_GroupIndex, uint3 groupId : SV_GroupID) +{ + // Load input from buffer written in previous pass. + DataType value = 0; + if (globalThreadId.x < gElems) value = gInputBuffer[globalThreadId.x]; + + reduce(value, groupId.x, groupThreadIdx); +} diff --git a/Framework/Source/Utils/Math/ParallelReduction.h b/Source/Falcor/Utils/Algorithm/ParallelReduction.h similarity index 84% rename from Framework/Source/Utils/Math/ParallelReduction.h rename to Source/Falcor/Utils/Algorithm/ParallelReduction.h index 4c9e017f6..0aef61e0a 100644 --- a/Framework/Source/Utils/Math/ParallelReduction.h +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,19 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" -#include "Graphics/FullScreenPass.h" -#include "Graphics/Program/ProgramVars.h" -#include "API/FBO.h" -#include "API/Sampler.h" -#include "API/CopyContext.h" +#include "Core/API/CopyContext.h" +#include "RenderGraph/BasePasses/FullScreenPass.h" namespace Falcor { - class RenderContext; - class Texture; - - class ParallelReduction + class dlldecl ParallelReduction { public: using UniquePtr = std::unique_ptr; @@ -51,9 +44,8 @@ namespace Falcor private: ParallelReduction(Type reductionType, uint32_t readbackLatency, uint32_t width, uint32_t height, uint32_t sampleCount); - FullScreenPass::UniquePtr mpFirstIterProg; - FullScreenPass::UniquePtr mpRestIterProg; - GraphicsVars::SharedPtr mpVars; + FullScreenPass::SharedPtr mpFirstIterProg; + FullScreenPass::SharedPtr mpRestIterProg; struct ResultData { @@ -69,4 +61,4 @@ namespace Falcor std::vector mpTmpResultFbo; static const uint32_t kTileSize = 16; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Utils/Algorithm/ParallelReductionType.h b/Source/Falcor/Utils/Algorithm/ParallelReductionType.h new file mode 100644 index 000000000..bea9e9141 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/ParallelReductionType.h @@ -0,0 +1,34 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +// Type defines shared between host and device. +#define FORMAT_TYPE_UNKNOWN 0 +#define FORMAT_TYPE_FLOAT 1 +#define FORMAT_TYPE_SINT 2 +#define FORMAT_TYPE_UINT 3 diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp new file mode 100644 index 000000000..10e11ec39 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "PrefixSum.h" +#include +#include + +namespace Falcor +{ + namespace + { + const char kShaderFile[] = "Utils/Algorithm/PrefixSum.cs.slang"; + const uint32_t kGroupSize = 1024; + } + + PrefixSum::SharedPtr PrefixSum::create() + { + SharedPtr ptr = SharedPtr(new PrefixSum()); + return ptr->init() ? ptr : nullptr; + } + + bool PrefixSum::init() + { + // Create shaders and state. + Program::DefineList defines = { {"GROUP_SIZE", std::to_string(kGroupSize)} }; + if (!(mpPrefixSumGroupProgram = ComputeProgram::createFromFile(kShaderFile, "groupScan", defines))) return false; + if (!(mpPrefixSumGroupVars = ComputeVars::create(mpPrefixSumGroupProgram.get()))) return false; + if (!(mpPrefixSumFinalizeProgram = ComputeProgram::createFromFile(kShaderFile, "finalizeGroups", defines))) return false; + if (!(mpPrefixSumFinalizeVars = ComputeVars::create(mpPrefixSumFinalizeProgram.get()))) return false; + + mpComputeState = ComputeState::create(); + assert(mpComputeState); + + // Create and bind buffer for per-group sums. + mpPrefixGroupSums = Buffer::create(kGroupSize * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr); + if (!mpPrefixGroupSums) return false; + + mpPrefixSumGroupVars["gPrefixGroupSums"] = mpPrefixGroupSums; + mpPrefixSumFinalizeVars["gPrefixGroupSums"] = mpPrefixGroupSums; + + return true; + } + + bool PrefixSum::execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t elementCount, uint32_t* pTotalSum, Buffer::SharedPtr pTotalSumBuffer, uint64_t pTotalSumOffset) + { + PROFILE("PrefixSum::execute"); + + assert(pRenderContext); + assert(elementCount > 0); + assert(pData && pData->getSize() >= elementCount * sizeof(uint32_t)); + + // The current implementation is limited to N groups of 2N elements, where N = thread group size. + // This is because we reuse the 1st pass to also compute the prefix sum across the thread groups. + // It is easy to generalize this by adding an extra pass to compute the per-group prefix sum if needed + // (with that large data sets, we probably want that for efficiency reasons anyway). + const uint32_t maxElementCount = kGroupSize * kGroupSize * 2; + if (elementCount > maxElementCount) + { + logError("PrefixSum::execute() - Maximum supported element count is " + std::to_string(maxElementCount) + ". Aborting."); + return false; + } + + // Compute number of thread groups in the first pass. Each thread operates on two elements. + const uint32_t numPrefixGroups = max(1u, div_round_up(elementCount, kGroupSize * 2)); + assert(numPrefixGroups > 0 && numPrefixGroups < kGroupSize); + + // Pass 1: compute per-thread group prefix sums. + { + // Clear group sums to zero. + pRenderContext->clearUAV(mpPrefixGroupSums->getUAV().get(), glm::uvec4(0)); + + // Set constants and data. + mpPrefixSumGroupVars["CB"]["gNumGroups"] = numPrefixGroups; + mpPrefixSumGroupVars["CB"]["gNumElems"] = elementCount; + mpPrefixSumGroupVars["gData"] = pData; + + mpComputeState->setProgram(mpPrefixSumGroupProgram); + pRenderContext->dispatch(mpComputeState.get(), mpPrefixSumGroupVars.get(), { numPrefixGroups, 1, 1 }); + } + + // Add UAV barriers for our buffers to make sure writes from the previous pass finish before the next pass. + // This is necessary since the buffers are bound as UAVs in both passes and there are no resource transitions. + pRenderContext->uavBarrier(pData.get()); + pRenderContext->uavBarrier(mpPrefixGroupSums.get()); + + // Pass 2: finalize prefix sum by adding the sums to the left to each group. + // This is only necessary if we have more than one group. + if (numPrefixGroups > 1) + { + // Compute number of thread groups. Each thread operates on one element. + // Note that we're skipping the first group of 2N elements, as no add is needed (their group sum is zero). + const uint dispatchSizeX = (numPrefixGroups - 1) * 2; + assert(dispatchSizeX > 0); + + // Set constants and data. + mpPrefixSumFinalizeVars["CB"]["gNumGroups"] = numPrefixGroups; + mpPrefixSumFinalizeVars["CB"]["gNumElems"] = elementCount; + mpPrefixSumFinalizeVars["gData"] = pData; + + mpComputeState->setProgram(mpPrefixSumFinalizeProgram); + pRenderContext->dispatch(mpComputeState.get(), mpPrefixSumFinalizeVars.get(), { dispatchSizeX, 1, 1 }); + } + + // Copy total sum to separate destination buffer, if specified. + if (pTotalSumBuffer) + { + if (pTotalSumOffset + 4 > pTotalSumBuffer->getSize()) + { + logError("PrefixSum::execute() - Results buffer is too small. Aborting."); + return false; + } + + assert(numPrefixGroups > 0); + uint64_t srcOffset = (numPrefixGroups - 1) * 4; + pRenderContext->copyBufferRegion(pTotalSumBuffer.get(), pTotalSumOffset, mpPrefixGroupSums.get(), srcOffset, 4); + } + + // Read back sum of all elements to the CPU, if requested. + if (pTotalSum) + { + uint32_t* pGroupSums = (uint32_t*)mpPrefixGroupSums->map(Buffer::MapType::Read); + assert(pGroupSums); + assert(numPrefixGroups > 0); + *pTotalSum = pGroupSums[numPrefixGroups - 1]; + mpPrefixGroupSums->unmap(); + } + + return true; + } +} diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang b/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang new file mode 100644 index 000000000..0568ab928 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.cs.slang @@ -0,0 +1,150 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Parallel prefix sum computed in place using exclusive scan. + + The host sets these defines: + GROUP_SIZE Thread group size, must be a power-of-two <= 1024. + + The implementation is based on G. Blelloch, "Vector Models for Data-Parallel Computing", MIT Press, 1990. + See CUDA code: http://http.developer.nvidia.com/GPUGems3/gpugems3_ch39.html + See also: http://www.umiacs.umd.edu/~ramani/cmsc828e_gpusci/ScanTalk.pdf +*/ + +cbuffer CB +{ + uint gNumGroups; ///< Number of groups we'll process, each group is 2N elements. + uint gNumElems; ///< Total number of elements. This does not have to be a multiple of the group size. +}; + +RWByteAddressBuffer gData; ///< Data buffer. +RWByteAddressBuffer gPrefixGroupSums; ///< One uint per group, each holds the sum of all elements in the group and to the left. + +groupshared uint gSharedData[2 * GROUP_SIZE]; ///< Temporary working buffer in shared memory for 2N elements. + + +/** Parallel prefix sum in shared memory over consecutive groups of 2N elements, + where N is the thread group size. + This shader reads from gData and writes one uint32_t per group to gPrefixGroupSums. +*/ +[numthreads(GROUP_SIZE, 1, 1)] +void groupScan(uint3 groupID : SV_GroupID, uint3 groupThreadID : SV_GroupThreadID) +{ + const uint thid = groupThreadID.x; // Local thread ID in the range 0..N-1. + const uint groupIdx = groupID.x; // Group index where each group represents 2N elements. + + // Load data for group into shared memory. Each thread loads two elements. + // Interleaved load at consecutive addresses can lead to 2x bank conflicts. + // It's probably better to load one element into each half of the array as we do here. + // We pad the data with zeros in shared memory if actual #elements is less than working set. + const uint idx = groupIdx * (2 * GROUP_SIZE) + thid; + gSharedData[thid] = idx < gNumElems ? gData.Load(idx * 4) : 0; + gSharedData[thid + GROUP_SIZE] = (idx + GROUP_SIZE) < gNumElems ? gData.Load((idx + GROUP_SIZE) * 4) : 0; + + // Reducation phase. + // We do log2(N)+1 iterations for d = 2^(N), 2^(N-1), .., 2, 1. + uint offset = 1; + for (uint d = GROUP_SIZE; d > 0; d >>= 1) + { + GroupMemoryBarrierWithGroupSync(); + + if (thid < d) + { + uint ai = offset * (2 * thid + 1) - 1; + uint bi = ai + offset; + + gSharedData[bi] += gSharedData[ai]; + } + offset *= 2; // offset = 1, 2, ... N + } + + GroupMemoryBarrierWithGroupSync(); + + // Compute prefix sum over groups. + // Since groups run out-of-order, we use atomics to add our group's sum to all relevent group sums. + // This can get slow for large inputs, but for moderate sized inputs (tens to hundreds of groups) it's probably still very fast. + // The alternative is to run an extra shader pass computing the prefix sum over the groups. + if (thid >= groupIdx && thid < gNumGroups) + { + uint sum = gSharedData[2 * GROUP_SIZE - 1]; + gPrefixGroupSums.InterlockedAdd(thid * 4, sum); + } + + GroupMemoryBarrierWithGroupSync(); + + // Zero out top element, this is required for down-sweep phase to work correctly. + // Only one thread in each group does this. + if (thid == 0) gSharedData[2 * GROUP_SIZE - 1] = 0; + + // Down-sweep phase. + // We do log2(N)+1 iterations for d = 1, 2, 4, ..., N. + for (uint d = 1; d <= GROUP_SIZE; d *= 2) + { + offset >>= 1; // offset = N, N/2, ..., 1 + + GroupMemoryBarrierWithGroupSync(); + + if (thid < d) + { + uint ai = offset * (2 * thid + 1) - 1; + uint bi = ai + offset; + + uint tmp = gSharedData[ai]; + gSharedData[ai] = gSharedData[bi]; + gSharedData[bi] += tmp; + } + } + + GroupMemoryBarrierWithGroupSync(); + + // Write results to memory. Lower half first then upper half. + if (idx < gNumElems) gData.Store(idx * 4, gSharedData[thid]); + if ((idx + GROUP_SIZE) < gNumElems) gData.Store((idx + GROUP_SIZE) * 4, gSharedData[thid + GROUP_SIZE]); +} + +/** Pass for finalizing a prefix sum computed over multiple thread groups. + Each thread here operates on one element of the data buffer. + Note that we're skipping the first N elements as those don't need to be added + (their group's prefix sum is zero). +*/ +[numthreads(GROUP_SIZE, 1, 1)] +void finalizeGroups(uint3 groupID : SV_GroupID, uint3 groupThreadID : SV_GroupThreadID) +{ + const uint thid = groupThreadID.x; // Local thread ID in the range 0..N-1. + const uint groupIdx = groupID.x; // Group index where each group represents N elements (skipping first 2N elements). + + uint sum = gPrefixGroupSums.Load((groupIdx >> 1) * 4); + uint globalIdx = (groupIdx * GROUP_SIZE) + thid + 2 * GROUP_SIZE; // Skip first 2N elements. + + if (globalIdx < gNumElems) + { + uint addr = globalIdx * 4; + uint elem = gData.Load(addr); + gData.Store(addr, elem + sum); + } +} diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.h b/Source/Falcor/Utils/Algorithm/PrefixSum.h new file mode 100644 index 000000000..7b2fab4f5 --- /dev/null +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.h @@ -0,0 +1,74 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Core/API/Buffer.h" +#include "Core/State/ComputeState.h" +#include "Core/Program/ComputeProgram.h" +#include "Core/Program/ProgramVars.h" + +namespace Falcor +{ + /** Computes the parallel prefix sum on the GPU. + + The prefix sum is computed in place using exclusive scan. + Each new element is y[i] = x[0] + ... + x[i-1], for i=1..N and y[0] = 0. + */ + class dlldecl PrefixSum : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; + virtual ~PrefixSum() = default; + + static SharedPtr create(); + + /** Computes the parallel prefix sum over an array of uint32_t elements. + \param[in] pRenderContext The render context. + \param[in] pData The buffer to compute prefix sum over. + \param[in] elementCount Number of elements to compute prefix sum over. + \param[out] pTotalSum (Optional) The sum of all elements is stored to this variable if it is non-null. Note that this requires a GPU sync! + \param[in] pTotalSumBuffer (Optional) Buffer on the GPU to which the total sum is copied (uint32_t). + \param[in] pTotalSumOffset (Optional) Byte offset into pTotalSumBuffer to where the sum should be written. + */ + bool execute(RenderContext* pRenderContext, Buffer::SharedPtr pData, uint32_t elementCount, uint32_t* pTotalSum = nullptr, Buffer::SharedPtr pTotalSumBuffer = nullptr, uint64_t pTotalSumOffset = 0); + + protected: + PrefixSum() = default; + bool init(); + + ComputeState::SharedPtr mpComputeState; + + ComputeProgram::SharedPtr mpPrefixSumGroupProgram; + ComputeVars::SharedPtr mpPrefixSumGroupVars; + + ComputeProgram::SharedPtr mpPrefixSumFinalizeProgram; + ComputeVars::SharedPtr mpPrefixSumFinalizeVars; + + Buffer::SharedPtr mpPrefixGroupSums; ///< Temporary buffer for prefix sum computation. + }; +} diff --git a/Source/Falcor/Utils/AlignedAllocator.h b/Source/Falcor/Utils/AlignedAllocator.h new file mode 100644 index 000000000..848f0e57a --- /dev/null +++ b/Source/Falcor/Utils/AlignedAllocator.h @@ -0,0 +1,155 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include +#include + +namespace Falcor +{ + + /** Utility class for aligned memory allocations on the GPU. + + AlignedAllocator can enforce various alignment requirements, + including minimum byte alignment and (optionally) that + allocated objects don't span two cache lines if they can fit + into one. Note that it's intended to be used to manage GPU + allocations and so it assumes that the base pointer starts at a + cache line. As such, it doesn't provide any alignment + guarantees on the CPU side (where it doesn't matter anyway). + */ + class AlignedAllocator + { + public: + /** Sets the minimum alignment for allocated objects. If a value of + zero is provided, no additional alignment is performed. + */ + void setMinimumAlignment(int minAlignment) + { + assert(minAlignment == 0 || isPowerOf2(minAlignment)); + mMinAlignment = minAlignment; + } + + /** Sets the cache line size so that allocations can be aligned so + that they don't span multiple cache lines (if possible). If a + value of zero is provided, then the allocator doesn't prevent + objects from spanning cache lines. + */ + void setCacheLineSize(int cacheLineSize) + { + assert(cacheLineSize == 0 || isPowerOf2(cacheLineSize)); + mCacheLineSize = cacheLineSize; + } + + /** Allocates an object of given type and executes its constructor. + \param[in] args Arguments to pass to the constructor. + \return pointer to allocated object. + */ + template T* allocate(Args&&... args) + { + const size_t size = sizeof(T); + computeAndAllocatePadding(size); + void* ptr = allocInternal(size); + return new (ptr) T(std::forward(args)...); + } + + /** Allocates an object of given type, potentially including additional memory at + the end of it, and executes its constructor. + \param[in] size Amount of memory to allocate. Must be >= sizeof(T). + \param[in] args Arguments to pass to the constructor. + \return pointer to allocated object. + */ + template T* allocateSized(size_t size, Args&&... args) + { + assert(size >= sizeof(T)); + computeAndAllocatePadding(size); + void* ptr = allocInternal(size); + return new (ptr) T(std::forward(args)...); + } + + void reserve(size_t size) { mBuffer.reserve(size); } + + void resize(size_t size) { mBuffer.resize(size, 0); } + + /** Returns the pointer to the start of the allocated buffer. + */ + void* getStartPointer() { return mBuffer.data(); } + const void* getStartPointer() const { return mBuffer.data(); } + + /** Returns of the offset of the given pointer inside the allocation buffer. + */ + size_t offsetOf(void* ptr) const + { + assert(ptr >= mBuffer.data() && ptr < mBuffer.data() + mBuffer.size()); + return static_cast(ptr) - mBuffer.data(); + } + + void reset() { mBuffer.clear(); } + + size_t getSize() const { return mBuffer.size(); } + size_t getCapacity() const { return mBuffer.capacity(); } + + private: + void computeAndAllocatePadding(size_t size) + { + const size_t currentOffset = mBuffer.size(); + + if (mCacheLineSize > 0) + { + const size_t cacheLineOffset = currentOffset % mCacheLineSize; + if (size < mCacheLineSize && cacheLineOffset + size > mCacheLineSize) + { + // The allocation is smaller than a cache line but + // would span two cache lines; move to the start of the + // next cache line. + const size_t pad = mCacheLineSize - cacheLineOffset; + (void)allocInternal(pad); + // There's need to worry about any further alignment + // issues now. + return; + } + } + + if (mMinAlignment > 0 && currentOffset % mMinAlignment) + { + // We're not at the minimum alignment; get aligned. + const size_t pad = mMinAlignment - (currentOffset % mMinAlignment); + (void)allocInternal(pad); + } + } + + void* allocInternal(size_t size) + { + auto iter = mBuffer.insert(mBuffer.end(), size, {}); + return &*iter; + } + + int mMinAlignment = 16; + int mCacheLineSize = 128; + std::vector mBuffer; + }; +} diff --git a/Framework/Source/ArgList.cpp b/Source/Falcor/Utils/ArgList.cpp similarity index 71% rename from Framework/Source/ArgList.cpp rename to Source/Falcor/Utils/ArgList.cpp index a8f95fbff..00c21cd9e 100644 --- a/Framework/Source/ArgList.cpp +++ b/Source/Falcor/Utils/ArgList.cpp @@ -1,7 +1,33 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" #include "ArgList.h" -#include "Framework.h" #include -#include namespace Falcor { diff --git a/Framework/Source/ArgList.h b/Source/Falcor/Utils/ArgList.h similarity index 95% rename from Framework/Source/ArgList.h rename to Source/Falcor/Utils/ArgList.h index dba4ca6df..a6fbf7c3a 100644 --- a/Framework/Source/ArgList.h +++ b/Source/Falcor/Utils/ArgList.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,18 +26,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include -#include namespace Falcor { /** Parses command line arguments and stores them for look-up by the user. */ - class ArgList + class dlldecl ArgList { public: - class Arg + class dlldecl Arg { public: Arg(const std::string& s) : mValue(s) {} diff --git a/Framework/Source/Utils/BinaryFileStream.h b/Source/Falcor/Utils/BinaryFileStream.h similarity index 98% rename from Framework/Source/Utils/BinaryFileStream.h rename to Source/Falcor/Utils/BinaryFileStream.h index f56bb2c54..8c6f39b25 100644 --- a/Framework/Source/Utils/BinaryFileStream.h +++ b/Source/Falcor/Utils/BinaryFileStream.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -108,7 +108,7 @@ namespace Falcor \return Number of bytes remaining in the stream */ uint32_t getRemainingStreamSize() - { + { std::streamoff currentPos = mStream.tellg(); mStream.seekg(0, mStream.end); std::streamoff length = mStream.tellg(); diff --git a/Samples/Raytracing/PathTracer/Data/RasterPrimary.slang b/Source/Falcor/Utils/Color/ColorMap.slang similarity index 59% rename from Samples/Raytracing/PathTracer/Data/RasterPrimary.slang rename to Source/Falcor/Utils/Color/ColorMap.slang index b97266bda..db5447bfd 100644 --- a/Samples/Raytracing/PathTracer/Data/RasterPrimary.slang +++ b/Source/Falcor/Utils/Color/ColorMap.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,37 +25,37 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -import Shading; -import DefaultVS; -struct GBufferOut -{ - float4 posW : SV_TARGET0; - float4 normW : SV_TARGET1; - float4 bitangentW : SV_TARGET2; - float4 texC : SV_TARGET3; - float4 diffuseOpacity : SV_TARGET4; - float4 specRough : SV_TARGET5; - float4 emissive : SV_TARGET6; - float4 matlExtra : SV_TARGET7; -}; +/** Helpers for mapping scalar values to RGB color for visualization purposes. -/** Entry point for G-buffer rasterization pixel shader. + The input is clamped to [0,1] and mapped to a continuous color range. + The colormapJet() function matches the output of Matlab's 'jet' color map. */ -GBufferOut ps(VertexOut vsOut) -{ - ShadingData sd = prepareShadingData(vsOut, gMaterial, gCamera.posW); - GBufferOut gOut; - gOut.posW = float4(sd.posW, 1.f); - gOut.normW = float4(sd.N, 0.f); - gOut.bitangentW = float4(sd.B, 0.f); - gOut.texC = float4(sd.uv, 0.f, 0.f); - - gOut.diffuseOpacity = float4(sd.diffuse, sd.opacity); - gOut.specRough = float4(sd.specular, sd.linearRoughness); - gOut.emissive = float4(sd.emissive, 0.f); - gOut.matlExtra = float4(normalize(vsOut.normalW), 0.f); +/** Maps scalar value to grayscale RGB value. + Values outside the [0,1] range are clamped. + \param[in] x Scalar value. + \return float3 Continuous RGB color in range [0,1]. +*/ +float3 colormapGray(float x) +{ + float v = saturate(x); + return float3(v, v, v); +} - return gOut; +/** Maps scalar value to the commonly used 'jet' color map in Matlab. + Values outside the [0,1] range are clamped to the end points. + \param[in] x Scalar value. + \return float3 Continuous RGB color in range [0,1]. +*/ +float3 colormapJet(float x) +{ + // Code written in Matlab to match jet.m output: + //x = max(0, min(1, x)); + //R = 1.5 - abs(x - 0.75) * 4; + //G = 1.5 - abs(x - 0.50) * 4; + //B = 1.5 - abs(x - 0.25) * 4; + //y = [R G B]; + //y = max(0, min(1, y)); + return saturate(1.5 - abs(4 * clamp(x, 0, 1) - float3(3, 2, 1))); } diff --git a/Source/Falcor/Utils/Color/ColorUtils.h b/Source/Falcor/Utils/Color/ColorUtils.h new file mode 100644 index 000000000..07510911d --- /dev/null +++ b/Source/Falcor/Utils/Color/ColorUtils.h @@ -0,0 +1,217 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include + +/** Color conversion utility functions. + + Falcor currently assumes all input/outputs are in sRGB, which uses the + ITU-R Rec. BT.709 (Rec.709) color space. + We have conversion functions to/from CIE XYZ to do certain operations like + white point correction, color temperature conversion etc. + + Matlab matrices for convenience below (row major): + + RGB Rec.709 to CIE XYZ (derived from primaries and D65 whitepoint): + + M = [ 0.4123907992659595 0.3575843393838780 0.1804807884018343; + 0.2126390058715104 0.7151686787677559 0.0721923153607337; + 0.0193308187155918 0.1191947797946259 0.9505321522496608 ] + + CIE XYZ to LMS using the CAT02 transform (part of CIECAM02): + + M = [ 0.7328 0.4296 -0.1624; + -0.7036 1.6975 0.0061; + 0.0030 0.0136 0.9834 ] + + CIE XYZ to LMS using the Bradford transform (part of the original CIECAM97 model): + + M = [ 0.8951 0.2664 -0.1614; + -0.7502 1.7135 0.0367; + 0.0389 -0.0685 1.0296 ] + + Note: glm is column major, so the pre-defined matrices below are transposed. + +*/ + +namespace Falcor +{ + // Transform from RGB color in Rec.709 to CIE XYZ. + static const glm::float3x3 kColorTransform_RGBtoXYZ_Rec709 = + { + 0.4123907992659595, 0.2126390058715104, 0.0193308187155918, + 0.3575843393838780, 0.7151686787677559, 0.1191947797946259, + 0.1804807884018343, 0.0721923153607337, 0.9505321522496608 + }; + + // Transform from XYZ color to RGB in Rec.709. + static const glm::float3x3 kColorTransform_XYZtoRGB_Rec709 = + { + 3.2409699419045213, -0.9692436362808798, 0.0556300796969936, + -1.5373831775700935, 1.8759675015077206, -0.2039769588889765, + -0.4986107602930033, 0.0415550574071756, 1.0569715142428784 + }; + + // Transform from CIE XYZ to LMS using the CAT02 transform. + static const glm::float3x3 kColorTransform_XYZtoLMS_CAT02 = + { + 0.7328, -0.7036, 0.0030, + 0.4296, 1.6975, 0.0136, + -0.1624, 0.0061, 0.9834 + }; + + // Transform from LMS to CIE XYZ using the inverse CAT02 transform. + static const glm::float3x3 kColorTransform_LMStoXYZ_CAT02 = + { + 1.096123820835514, 0.454369041975359, -0.009627608738429, + -0.278869000218287, 0.473533154307412, -0.005698031216113, + 0.182745179382773, 0.072097803717229, 1.015325639954543 + }; + + // Transform from CIE XYZ to LMS using the Bradford transform. + static const glm::float3x3 kColorTransform_XYZtoLMS_Bradford = + { + 0.8951, -0.7502, 0.0389, + 0.2664, 1.7135, -0.0685, + -0.1614, 0.0367, 1.0296 + }; + + // Transform from LMS to CIE XYZ using the inverse Bradford transform. + static const glm::float3x3 kColorTransform_LMStoXYZ_Bradford = + { + 0.98699290546671214, 0.43230526972339445, -0.00852866457517732, + -0.14705425642099013, 0.51836027153677744, 0.04004282165408486, + 0.15996265166373122, 0.04929122821285559, 0.96848669578754998 + }; + + /** Transforms an RGB color in Rec.709 to CIE XYZ. + */ + static glm::float3 RGBtoXYZ_Rec709(glm::float3 c) + { + return kColorTransform_RGBtoXYZ_Rec709 * c; + } + + /** Transforms an XYZ color to RGB in Rec.709. + */ + static glm::float3 XYZtoRGB_Rec709(glm::float3 c) + { + return kColorTransform_XYZtoRGB_Rec709 * c; + } + + /** Converts (chromaticities, luminance) to XYZ color. + */ + static glm::float3 xyYtoXYZ(float x, float y, float Y) + { + return glm::float3(x * Y / y, Y, (1.f - x - y) * Y / y); + } + + /** Transforms color temperature of a blackbody emitter to color in CIE XYZ. + This function uses an approximation based on piecewise rational polynomials: + Kang et al., Design of Advanced Color Temperature Control System for HDTV Applications, 2002. + https://pdfs.semanticscholar.org/cc7f/c2e67601ccb1a8fec048c9b78a4224c34d26.pdf + + \param[in] T Color temperature in degrees Kelvin, supported range is 1667K to 25000K. + \param[in] Y Luminance. + \return CIE XYZ color. + */ + static glm::float3 colorTemperatureToXYZ(float T, float Y = 1.f) + { + if (T < 1667.f || T > 25000.f) + { + logError("colorTemperatureToXYZ() - T is out of range"); + return glm::float3(0, 0, 0); + } + + // We do the computations in double + double t = T; + double t2 = t * t; + double t3 = t * t * t; + + double xc = 0.0; + if (T < 4000.f) + { + xc = -0.2661239e9 / t3 - 0.2343580e6 / t2 + 0.8776956e3 / t + 0.179910; + } + else + { + xc = -3.0258469e9 / t3 + 2.1070379e6 / t2 + 0.2226347e3 / t + 0.240390; + } + + double x = xc; + double x2 = x * x; + double x3 = x * x * x; + + double yc = 0.0; + if (T < 2222.f) + { + yc = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.20219683; + } + else if (T < 4000.f) + { + yc = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867; + } + else + { + yc = +3.0817580 * x3 - 5.87338670 * x2 + 3.75112997 * x - 0.37001483; + } + + // Return as XYZ color. + return xyYtoXYZ((float)xc, (float)yc, Y); + } + + /** Calculates the 3x3 matrix that performs white balancing in RGB Rec.709 space + to a target color temperature. + + The function uses the von Kries transform, i.e. a diagonal scaling matrix in LMS space. + The default LMS transform is CAT02 (part of CIECAM02). + + The transform is chosen so that the D65 white point is exactly preserved at T=6500K. + Note that the transformed RGB can be out-of-gamut in Rec.709 (negative values + are possible) depending on T, so it is advisable to gamut clamp the result. + + \param[in] T Target color temperature (K). + \return 3x3 matrix M, which transforms linear RGB in Rec.709 using c' = M * c. + */ + static glm::float3x3 calculateWhiteBalanceTransformRGB_Rec709(float T) + { + static const glm::float3x3 MA = kColorTransform_XYZtoLMS_CAT02 * kColorTransform_RGBtoXYZ_Rec709; // RGB -> LMS + static const glm::float3x3 invMA = kColorTransform_XYZtoRGB_Rec709 * kColorTransform_LMStoXYZ_CAT02; // LMS -> RGB + + // Compute destination reference white in LMS space. + static const glm::float3 wd = kColorTransform_XYZtoLMS_CAT02 * colorTemperatureToXYZ(6500.f); + + // Compute source reference white in LMS space. + const glm::float3 ws = kColorTransform_XYZtoLMS_CAT02 * colorTemperatureToXYZ(T); + + // Derive final 3x3 transform in RGB space. + glm::float3 scale = wd / ws; + glm::float3x3 D = glm::diagonal3x3(scale); + + return invMA * D * MA; + } +} diff --git a/Source/Falcor/Utils/Debug/DebugConsole.h b/Source/Falcor/Utils/Debug/DebugConsole.h new file mode 100644 index 000000000..840f023da --- /dev/null +++ b/Source/Falcor/Utils/Debug/DebugConsole.h @@ -0,0 +1,112 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include +#include + +namespace Falcor +{ + /** Opens a console window and redirects std::cout, std::cerr, and std::cin there. + Upon destruction of the object, the console is closed and the streams are restored to the previous state. + */ + class DebugConsole + { + public: + /** Opens a console window. The destructor closes it again. + \param[in] waitForKey If true, the console waits for a key press before closing. + */ + DebugConsole(bool waitForKey = true) + : mWaitForKey(waitForKey) + { + // Open console window + AllocConsole(); + + // Redirect cout/cerr/cin streams to our console window + mPrevCout = std::cout.rdbuf(); + mCout.open("CONOUT$"); + std::cout.rdbuf(mCout.rdbuf()); + + mPrevCerr = std::cerr.rdbuf(); + mCerr.open("CONERR$"); + std::cerr.rdbuf(mCerr.rdbuf()); + + mPrevCin = std::cin.rdbuf(); + mCin.open("CONIN$"); + std::cin.rdbuf(mCin.rdbuf()); + + // Redirect stdout for printf() to our console + //freopen_s(&mFp, "CONOUT$", "w", stdout); + //std::cout.clear(); + } + + virtual ~DebugConsole() + { + flush(); + if (mWaitForKey) + { + pause(); + } + + // Restore the streams + std::cin.rdbuf(mPrevCin); + std::cerr.rdbuf(mPrevCerr); + std::cout.rdbuf(mPrevCout); + + // Restore stdout to default + //freopen("OUT", "w", stdout); + //fclose(mFp); + + // Close console window + FreeConsole(); + } + + void pause() const + { + std::cout << "Press any key to continue..." << std::endl; + flush(); + char c = std::cin.get(); + } + + void flush() const + { + std::cout.flush(); + std::cerr.flush(); + } + + private: + std::ofstream mCout; + std::ofstream mCerr; + std::ifstream mCin; + std::streambuf* mPrevCout; + std::streambuf* mPrevCerr; + std::streambuf* mPrevCin; + //FILE* mFp = nullptr; + + bool mWaitForKey = true; + }; +} diff --git a/Source/Falcor/Utils/Debug/PixelDebug.cpp b/Source/Falcor/Utils/Debug/PixelDebug.cpp new file mode 100644 index 000000000..abcb89e9b --- /dev/null +++ b/Source/Falcor/Utils/Debug/PixelDebug.cpp @@ -0,0 +1,237 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "PixelDebug.h" +#include +#include + +namespace Falcor +{ + PixelDebug::SharedPtr PixelDebug::create(uint32_t logSize) + { + return SharedPtr(new PixelDebug(logSize)); + } + + void PixelDebug::begin(RenderContext* pRenderContext, const RtProgram::SharedPtr& pProgram, const RtProgramVars::SharedPtr& pVars, const glm::uvec2& frameDim) + { + mFrameDim = frameDim; + if (mRunning) + { + logError("PixelDebug::end() - Logging is already running, did you forget to call end()? Ignoring call."); + return; + } + mRunning = true; + + // Reset previous data. + mPixelLogData.clear(); + mAssertLogData.clear(); + mDataValid = false; + mWaitingForData = false; + + // Configure program. + pProgram->addDefine("_ENABLE_PIXEL_DEBUG", mEnabled ? "1" : "0"); + + if (mEnabled) + { + // Prepare log buffers. + if (!mpPixelLog || mpPixelLog->getElementCount() != mLogSize) + { + // Allocate GPU buffers. + mpPixelLog = StructuredBuffer::create(pProgram->getRayGenProgram().get(), "gPixelLog", mLogSize); + if (!mpPixelLog) throw std::exception("Failed to create StructuredBuffer object"); + if (mpPixelLog->getElementSize() != sizeof(PixelLogValue)) throw std::runtime_error("Struct PixelLogValue size mismatch between CPU/GPU"); + + mpAssertLog = StructuredBuffer::create(pProgram->getRayGenProgram().get(), "gAssertLog", mLogSize); + if (!mpAssertLog) throw std::exception("Failed to create StructuredBuffer object"); + if (mpAssertLog->getElementSize() != sizeof(AssertLogValue)) throw std::runtime_error("Struct AssertLogValue size mismatch between CPU/GPU"); + + // Allocate staging buffers for readback. These are shared, the data is stored consecutively. + mpCounterBuffer = Buffer::create(2 * sizeof(uint32_t), ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpDataBuffer = Buffer::create(mpPixelLog->getSize() + mpAssertLog->getSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); + } + + pRenderContext->clearUAVCounter(mpPixelLog, 0); + pRenderContext->clearUAVCounter(mpAssertLog, 0); + + auto pGlobalVars = pVars->getGlobalVars(); + pGlobalVars["gPixelLog"] = mpPixelLog; + pGlobalVars["gAssertLog"] = mpAssertLog; + pGlobalVars["PixelDebugCB"]["gPixelLogSelected"] = mSelectedPixel; + pGlobalVars["PixelDebugCB"]["gPixelLogSize"] = mLogSize; + pGlobalVars["PixelDebugCB"]["gAssertLogSize"] = mLogSize; + } + } + + void PixelDebug::end(RenderContext* pRenderContext) + { + if (!mRunning) + { + logError("PixelDebug::end() - Logging is not running, did you forget to call begin()? Ignoring call."); + return; + } + mRunning = false; + + if (mEnabled) + { + // Copy logged data to staging buffers. + pRenderContext->copyBufferRegion(mpCounterBuffer.get(), 0, mpPixelLog->getUAVCounter().get(), 0, 4); + pRenderContext->copyBufferRegion(mpCounterBuffer.get(), 4, mpAssertLog->getUAVCounter().get(), 0, 4); + pRenderContext->copyBufferRegion(mpDataBuffer.get(), 0, mpPixelLog.get(), 0, mpPixelLog->getSize()); + pRenderContext->copyBufferRegion(mpDataBuffer.get(), mpPixelLog->getSize(), mpAssertLog.get(), 0, mpAssertLog->getSize()); + + // Create fence first time we need it. + if (!mpFence) mpFence = GpuFence::create(); + + // Submit command list and insert signal. + pRenderContext->flush(false); + mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + + mWaitingForData = true; + } + } + + void PixelDebug::renderUI(Gui::Widgets& widget) + { + if (mRunning) + { + logError("PixelDebug::renderUI() - Logging is running, call end() before renderUI(). Ignoring call."); + return; + } + + // Configure logging. + widget.checkbox("Pixel debug", mEnabled); + widget.tooltip("Enables shader debugging.\n\n" + "Left-mouse click on a pixel to select it.\n" + "Use print() in the shader to print values of basic types (int, float2, etc.) for the selected pixel.\n" + "Use assert() in the shader to test a condition.", true); + if (mEnabled) + { + widget.var("Selected pixel", mSelectedPixel); + } + + // Fetch stats and show log if available. + copyDataToCPU(); + if (mDataValid) + { + std::ostringstream oss; + + // Print list of printed values. + oss << "Pixel log:" << (mPixelLogData.empty() ? " \n" : "\n"); + for (auto v : mPixelLogData) + { + // Parse value and convert to string. + if (v.count > 1) oss << "("; + for (uint32_t i = 0; i < v.count; i++) + { + uint32_t bits = v.data[i]; + switch ((PixelLogValueType)v.type) + { + case PixelLogValueType::Bool: + oss << (bits != 0) ? "true" : "false"; + break; + case PixelLogValueType::Int: + oss << (int32_t)bits; + break; + case PixelLogValueType::Uint: + oss << bits; + break; + case PixelLogValueType::Float: + // TODO: Replace by std::bit_cast in C++20 when that is available. + oss << *reinterpret_cast(&bits); + break; + default: + oss << "INVALID VALUE"; + break; + } + if (i + 1 < v.count) oss << ", "; + } + if (v.count > 1) oss << ")"; + oss << "\n"; + } + + // Print list of asserts. + if (!mAssertLogData.empty()) + { + oss << "\n"; + for (auto v : mAssertLogData) + { + oss << "assert at (" << v.launchIndex.x << ", " << v.launchIndex.y << ", " << v.launchIndex.z << ")\n"; + logWarning("Shader assert at launch index (" + std::to_string(v.launchIndex.x) + ", " + std::to_string(v.launchIndex.y) + ", " + std::to_string(v.launchIndex.z) + ")"); + } + } + + widget.text(oss.str().c_str()); + } + } + + bool PixelDebug::onMouseEvent(const MouseEvent& mouseEvent) + { + if (mEnabled) + { + if (mouseEvent.type == MouseEvent::Type::LeftButtonDown) + { + mSelectedPixel = glm::uvec2(mouseEvent.pos * glm::vec2(mFrameDim)); + return true; + } + } + return false; + } + + void PixelDebug::copyDataToCPU() + { + assert(!mRunning); + if (mWaitingForData) + { + // Wait for signal. + mpFence->syncCpu(); + mWaitingForData = false; + + if (mEnabled) + { + // Map counter buffer. This tells us how many print() and assert() calls were made. + uint32_t* uavCounters = (uint32_t*)mpCounterBuffer->map(Buffer::MapType::Read); + const size_t printCount = std::min(mpPixelLog->getElementCount(), (size_t)uavCounters[0]); + const size_t assertCount = std::min(mpAssertLog->getElementCount(), (size_t)uavCounters[1]); + mpCounterBuffer->unmap(); + + // Map the data buffer and copy the relevant sections. + byte* pLog = (byte*)mpDataBuffer->map(Buffer::MapType::Read); + + mPixelLogData.resize(printCount); + for (size_t i = 0; i < printCount; i++) mPixelLogData[i] = ((PixelLogValue*)pLog)[i]; + pLog += mpPixelLog->getSize(); + + mAssertLogData.resize(assertCount); + for (size_t i = 0; i < assertCount; i++) mAssertLogData[i] = ((AssertLogValue*)pLog)[i]; + + mpDataBuffer->unmap(); + mDataValid = true; + } + } + } +} diff --git a/Source/Falcor/Utils/Debug/PixelDebug.h b/Source/Falcor/Utils/Debug/PixelDebug.h new file mode 100644 index 000000000..0685838ae --- /dev/null +++ b/Source/Falcor/Utils/Debug/PixelDebug.h @@ -0,0 +1,97 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "PixelDebugTypes.h" +#include "Raytracing/RtProgram/RtProgram.h" +#include "Raytracing/RtProgramVars.h" + +namespace Falcor +{ + /** Helper class for shader debugging using print() and assert(). + + Host-side integration: + - Create PixelDebug object + - Call begin()/end() before and after the ray tracing dispatch. + - Call onMouseEvent() and renderUI() from the respective callbacks in the render pass. + + Runtime usage: + - Import PixelDebug.slang in your ray tracing shaders. + - Use print() in the shader to output values for the selected pixel. + All basic types (e.g. bool, int3, float2, uint4) are supported. + - Click the left mouse button (or edit the coords) to select a pixel. + - Use assert() in the shader to test a condition for being true. + All pixels are tested, and failed asserts logged. The coordinates + of asserts that trigger can be used with print() to debug further. + + The shader code is disabled (using macros) when debugging is off. + When enabled, async readback is used but expect a minor perf loss. + */ + class dlldecl PixelDebug + { + public: + using SharedPtr = std::shared_ptr; + virtual ~PixelDebug() = default; + + /** Creates object. + \param[in] logSize Number of shader print() and assert() statements per frame. + */ + static SharedPtr create(uint32_t logSize = 100); + + void begin(RenderContext* pRenderContext, const RtProgram::SharedPtr& pProgram, const RtProgramVars::SharedPtr& pVars, const glm::uvec2& frameDim); + void end(RenderContext* pRenderContext); + void renderUI(Gui::Widgets& widget); + bool onMouseEvent(const MouseEvent& mouseEvent); + + protected: + PixelDebug(uint32_t logSize) : mLogSize(logSize) {} + void copyDataToCPU(); + + // Internal state + StructuredBuffer::SharedPtr mpPixelLog; ///< Pixel log on the GPU with UAV counter. + StructuredBuffer::SharedPtr mpAssertLog; ///< Assert log on the GPU with UAV counter. + Buffer::SharedPtr mpCounterBuffer; ///< Staging buffer for async readback of UAV counters. + Buffer::SharedPtr mpDataBuffer; ///< Staging buffer for async readback of logged data. + GpuFence::SharedPtr mpFence; ///< GPU fence for sychronizing readback. + + // Configuration + bool mEnabled = false; ///< Enables debugging features. + glm::uvec2 mSelectedPixel = { 0, 0 }; ///< Currently selected pixel. + + // Runtime data + glm::uvec2 mFrameDim = { 0, 0 }; + + bool mRunning = false; ///< True when data collection is running (inbetween begin()/end() calls). + bool mWaitingForData = false; ///< True if we are waiting for data to become available on the GPU. + bool mDataValid = false; ///< True if data has been read back and is valid. + + std::vector mPixelLogData; ///< Pixel log data read back from the GPU. + std::vector mAssertLogData; ///< Assert log data read back from the GPU. + + const uint32_t mLogSize = 0; ///< Size of the log buffers in elements. + }; +} diff --git a/Source/Falcor/Utils/Debug/PixelDebug.slang b/Source/Falcor/Utils/Debug/PixelDebug.slang new file mode 100644 index 000000000..c20b38c01 --- /dev/null +++ b/Source/Falcor/Utils/Debug/PixelDebug.slang @@ -0,0 +1,122 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** GPU side implementation of pixel debugging utils. + + Note that the print() functions must be called from a ray tracing + program as they use DispatchRaysIndex() to identify the current pixel. + + The host sets the following defines: + + _ENABLE_PIXEL_DEBUG Nonzero when pixel debugging is enabled. + +*/ + +#include "PixelDebugTypes.h" + +shared cbuffer PixelDebugCB +{ + uint2 gPixelLogSelected; // Currently selected pixel to log. + uint gPixelLogSize; // Number of elements in the output buffer. + uint gAssertLogSize; +}; + +shared RWStructuredBuffer gPixelLog; +shared RWStructuredBuffer gAssertLog; + +#if _ENABLE_PIXEL_DEBUG + + /** Define overloaded 'void print(Type x)' functions. Each takes a different + basic type as parameter and appends it in encoded form to the log. + */ + #define PRINT_FUNC(Type, Count, ValueType) \ + void print(vector v) \ + { \ + if (all(DispatchRaysIndex().xy == gPixelLogSelected)) \ + { \ + uint i = gPixelLog.IncrementCounter(); \ + if (i < gPixelLogSize) \ + { \ + PixelLogValue val; \ + val.type = (uint)ValueType; \ + val.count = Count; \ + for (int j = 0; j < 4; j++) \ + { \ + val.data[j] = j < Count ? asuint(v[j]) : 0; \ + } \ + gPixelLog[i] = val; \ + } \ + } \ + } + + /** Ray tracing shader 'assert(bool condition)' function. + If condition is false, the launch index of the assert is recorded in the log. + */ + void assert(bool condition) + { + if (!condition) + { + uint i = gAssertLog.IncrementCounter(); + if (i < gAssertLogSize) + { + AssertLogValue val; + val.launchIndex = DispatchRaysIndex(); + gAssertLog[i] = val; + } + } + } + +#else + + /** Define null functions if debugging is disabled. + */ + #define PRINT_FUNC(Type, Count, DataType) \ + void print(vector v) {} + + void assert(bool condition) {} + +#endif // !_ENABLE_PIXEL_DEBUG + +PRINT_FUNC(bool, 1, PixelLogValueType.Bool) +PRINT_FUNC(bool, 2, PixelLogValueType.Bool) +PRINT_FUNC(bool, 3, PixelLogValueType.Bool) +PRINT_FUNC(bool, 4, PixelLogValueType.Bool) +PRINT_FUNC(int, 1, PixelLogValueType.Int) +PRINT_FUNC(int, 2, PixelLogValueType.Int) +PRINT_FUNC(int, 3, PixelLogValueType.Int) +PRINT_FUNC(int, 4, PixelLogValueType.Int) +PRINT_FUNC(uint, 1, PixelLogValueType.Uint) +PRINT_FUNC(uint, 2, PixelLogValueType.Uint) +PRINT_FUNC(uint, 3, PixelLogValueType.Uint) +PRINT_FUNC(uint, 4, PixelLogValueType.Uint) +PRINT_FUNC(float, 1, PixelLogValueType.Float) +PRINT_FUNC(float, 2, PixelLogValueType.Float) +PRINT_FUNC(float, 3, PixelLogValueType.Float) +PRINT_FUNC(float, 4, PixelLogValueType.Float) + +#undef PRINT_FUNC diff --git a/Samples/Core/MultiPassPostProcess/Data/Luminance.ps.hlsl b/Source/Falcor/Utils/Debug/PixelDebugTypes.h similarity index 73% rename from Samples/Core/MultiPassPostProcess/Data/Luminance.ps.hlsl rename to Source/Falcor/Utils/Debug/PixelDebugTypes.h index 9d4488c0f..2dcb135ec 100644 --- a/Samples/Core/MultiPassPostProcess/Data/Luminance.ps.hlsl +++ b/Source/Falcor/Utils/Debug/PixelDebugTypes.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,23 +25,31 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#pragma once +#include "Data/HostDeviceData.h" -cbuffer PerImageCB : register(b0) +BEGIN_NAMESPACE_FALCOR + +/** Define the basic types that print() supports. +*/ +enum class PixelLogValueType { - Texture2D gTexture; - SamplerState gSampler; + Bool = 0, + Int, + Uint, + Float, }; -static const float3 gLuminance = float3(0.2126, 0.7152, 0.0722); - -float4 calcColor(float2 texC) +struct PixelLogValue { - float4 fragColor = gTexture.Sample(gSampler, texC); - fragColor.rgb = (dot(fragColor.rgb, gLuminance)).xxx; - return fragColor; -} + uint type; ///< Value type (see PixelLogValueType). + uint count; ///< Number of components (1-4). + uint4 data; ///< The data bits. The encoding is determined by the data type. +}; -float4 main(in float2 texC : TEXCOORD) : SV_TARGET +struct AssertLogValue { - return calcColor(texC); -} + uint3 launchIndex; ///< Launch index for the assert. +}; + +END_NAMESPACE_FALCOR diff --git a/Framework/Source/Utils/Bitmap.cpp b/Source/Falcor/Utils/Image/Bitmap.cpp similarity index 77% rename from Framework/Source/Utils/Bitmap.cpp rename to Source/Falcor/Utils/Image/Bitmap.cpp index 5205fab6f..5cdaefeea 100644 --- a/Framework/Source/Utils/Bitmap.cpp +++ b/Source/Falcor/Utils/Image/Bitmap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Bitmap.h" #include "FreeImage.h" -#include "Utils/Platform/OS.h" -#include "API/Device.h" -#include -#include "StringUtils.h" -#include "API/Texture.h" +#include "Core/API/Texture.h" +#include "Utils/StringUtils.h" namespace Falcor { @@ -46,17 +43,54 @@ namespace Falcor #else static bool isRGB32fSupported() { return false; } // FIX THIS #endif - const Bitmap* genError(const std::string& errMsg, const std::string& filename) + static const Bitmap* genError(const std::string& errMsg, const std::string& filename) { std::string err = "Error when loading image file " + filename + '\n' + errMsg + '.'; logError(err); return nullptr; } + /** Converts 96bpp to 128bpp RGBA without clamping. + Note that we can't use FreeImage_ConvertToRGBAF() as it clamps to [0,1]. + */ + static FIBITMAP* convertToRGBAF(FIBITMAP* pDib) + { + const unsigned width = FreeImage_GetWidth(pDib); + const unsigned height = FreeImage_GetHeight(pDib); + + auto pNew = FreeImage_AllocateT(FIT_RGBAF, width, height); + FreeImage_CloneMetadata(pNew, pDib); + + const unsigned src_pitch = FreeImage_GetPitch(pDib); + const unsigned dst_pitch = FreeImage_GetPitch(pNew); + + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(pDib); + BYTE* dst_bits = (BYTE*)FreeImage_GetBits(pNew); + + for (unsigned y = 0; y < height; y++) + { + const FIRGBF *src_pixel = (FIRGBF*)src_bits; + FIRGBAF* dst_pixel = (FIRGBAF*)dst_bits; + + for (unsigned x = 0; x < width; x++) + { + // Convert pixels directly, while adding a "dummy" alpha of 1.0 + dst_pixel[x].red = src_pixel[x].red; + dst_pixel[x].green = src_pixel[x].green; + dst_pixel[x].blue = src_pixel[x].blue; + dst_pixel[x].alpha = 1.0F; + + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + return pNew; + } + Bitmap::UniqueConstPtr Bitmap::createFromFile(const std::string& filename, bool isTopDown) { std::string fullpath; - if(findFileInDataDirectories(filename, fullpath) == false) + if (findFileInDataDirectories(filename, fullpath) == false) { msgBox("Error when loading image file " + filename + "\n. Can't find the file"); return nullptr; @@ -65,36 +99,36 @@ namespace Falcor FREE_IMAGE_FORMAT fifFormat = FIF_UNKNOWN; fifFormat = FreeImage_GetFileType(fullpath.c_str(), 0); - if(fifFormat == FIF_UNKNOWN) + if (fifFormat == FIF_UNKNOWN) { // Can't get the format from the file. Use file extension fifFormat = FreeImage_GetFIFFromFilename(fullpath.c_str()); - if(fifFormat == FIF_UNKNOWN) + if (fifFormat == FIF_UNKNOWN) { return UniqueConstPtr(genError("Image Type unknown", filename)); } } // Check the the library supports loading this image Type - if(FreeImage_FIFSupportsReading(fifFormat) == false) + if (FreeImage_FIFSupportsReading(fifFormat) == false) { return UniqueConstPtr(genError("Library doesn't support the file format", filename)); } // Read the DIB FIBITMAP* pDib = FreeImage_Load(fifFormat, fullpath.c_str()); - if(pDib == nullptr) + if (pDib == nullptr) { return UniqueConstPtr(genError("Can't read image file", filename)); } - // create the bitmap + // Create the bitmap auto pBmp = new Bitmap; pBmp->mHeight = FreeImage_GetHeight(pDib); pBmp->mWidth = FreeImage_GetWidth(pDib); - if(pBmp->mHeight == 0 || pBmp->mWidth == 0 || FreeImage_GetBits(pDib) == nullptr) + if (pBmp->mHeight == 0 || pBmp->mWidth == 0 || FreeImage_GetBits(pDib) == nullptr) { return UniqueConstPtr(genError("Invalid image", filename)); } @@ -132,7 +166,7 @@ namespace Falcor } // Convert the image to RGBX image - if(bpp == 24) + if (bpp == 24) { logWarning("Converting 24-bit texture to 32-bit"); bpp = 32; @@ -144,7 +178,7 @@ namespace Falcor { logWarning("Converting 96-bit texture to 128-bit"); bpp = 128; - auto pNew = FreeImage_ConvertToRGBAF(pDib); + auto pNew = convertToRGBAF(pDib); FreeImage_Unload(pDib); pDib = pNew; } @@ -228,39 +262,41 @@ namespace Falcor bool showHdr = true; bool showLdr = true; - if(format != ResourceFormat::Unknown) + if (format != ResourceFormat::Unknown) { - FormatType type = getFormatType(format); - uint32_t bitsPerTexel = getFormatBytesPerBlock(format); - - showHdr = type == FormatType::Float && (bitsPerTexel == 16 || bitsPerTexel == 12); + showHdr = getFormatType(format) == FormatType::Float; showLdr = !showHdr; } if (showHdr) { - filters.push_back({"hdr", "High Dynamic Range"}); - filters.push_back({"exr", "High Dynamic Range"}); - filters.push_back({"pfm", "Portable Float Map"}); + filters.push_back({ "exr", "High Dynamic Range" }); + filters.push_back({ "pfm", "Portable Float Map" }); } - if(showLdr) + if (showLdr) { filters.push_back({ "png", "Portable Network Graphics" }); filters.push_back({ "jpg", "JPEG" }); filters.push_back({ "bmp", "Bitmap Image File" }); filters.push_back({ "tga", "Truevision Graphics Adapter" }); } + + // List of formats we can only load from + if (format == ResourceFormat::Unknown) + { + filters.push_back({ "hdr", "High Dynamic Range" }); + } return filters; } - std::string Bitmap::getFilExtFromResourceFormat(ResourceFormat format) + std::string Bitmap::getFileExtFromResourceFormat(ResourceFormat format) { auto filters = getFileDialogFilters(format); return filters.front().ext; } - void Bitmap::saveImageDialog(const Texture::SharedPtr& pTexture) + void Bitmap::saveImageDialog(Texture* pTexture) { std::string filePath; auto supportExtensions = getFileDialogFilters(pTexture->getFormat()); @@ -273,15 +309,30 @@ namespace Falcor } } + std::vector rgba16torgba32(const void* pData, uint32_t width, uint32_t height) + { + std::vector newData(width*height * sizeof(float) * 4); + glm::detail::hdata* f16data = (glm::detail::hdata*)pData; + float* f32data = (float*)newData.data(); + uint32_t count = width * height * 4; + + for (uint32_t i = 0; i < count; ++i) + { + f32data[i] = glm::detail::toFloat32(f16data[i]); + } + + return newData; + } + void Bitmap::saveImage(const std::string& filename, uint32_t width, uint32_t height, FileFormat fileFormat, ExportFlags exportFlags, ResourceFormat resourceFormat, bool isTopDown, void* pData) { - if(pData == nullptr) + if (pData == nullptr) { logError("Bitmap::saveImage provided no data to save."); return; } - if(is_set(exportFlags, ExportFlags::Uncompressed) && is_set(exportFlags, ExportFlags::Lossy)) + if (is_set(exportFlags, ExportFlags::Uncompressed) && is_set(exportFlags, ExportFlags::Lossy)) { logError("Bitmap::saveImage incompatible flags: lossy cannot be combined with uncompressed."); return; @@ -310,15 +361,23 @@ namespace Falcor if (fileFormat == Bitmap::FileFormat::PfmFile || fileFormat == Bitmap::FileFormat::ExrFile) { - if(bytesPerPixel != 16 && bytesPerPixel != 12) + std::vector f16data; + if (resourceFormat == ResourceFormat::RGBA16Float) + { + f16data = rgba16torgba32(pData, width, height); + pData = f16data.data(); + resourceFormat = ResourceFormat::RGBA32Float; + bytesPerPixel = 16; + } + else if (bytesPerPixel != 16 && bytesPerPixel != 12) { - logError("Bitmap::saveImage supports only 32-bit/channel RGB/RGBA images as PFM/EXR files."); + logError("Bitmap::saveImage supports only 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); return; } const bool exportAlpha = is_set(exportFlags, ExportFlags::ExportAlpha); - if(fileFormat == Bitmap::FileFormat::PfmFile) + if (fileFormat == Bitmap::FileFormat::PfmFile) { if (is_set(exportFlags, ExportFlags::Lossy)) { @@ -343,17 +402,17 @@ namespace Falcor pImage = FreeImage_AllocateT(exportAlpha ? FIT_RGBAF : FIT_RGBF, width, height); BYTE* head = (BYTE*)pData; - for(unsigned y = 0; y < height; y++) + for (unsigned y = 0; y < height; y++) { float* dstBits = (float*)FreeImage_GetScanLine(pImage, height - y - 1); - if(scanlineCopy) + if (scanlineCopy) { std::memcpy(dstBits, head, bytesPerPixel * width); } else { assert(exportAlpha == false); - for(unsigned x = 0; x < width; x++) + for (unsigned x = 0; x < width; x++) { dstBits[x*3 + 0] = (((float*)head)[x*4 + 0]); dstBits[x*3 + 1] = (((float*)head)[x*4 + 1]); @@ -363,7 +422,7 @@ namespace Falcor head += bytesPerPixel * width; } - if(fileFormat == Bitmap::FileFormat::ExrFile) + if (fileFormat == Bitmap::FileFormat::ExrFile) { flags = 0; if (is_set(exportFlags, ExportFlags::Uncompressed)) @@ -379,7 +438,7 @@ namespace Falcor else { FIBITMAP* pTemp = FreeImage_ConvertFromRawBits((BYTE*)pData, width, height, bytesPerPixel * width, bytesPerPixel * 8, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, isTopDown); - if(is_set(exportFlags, ExportFlags::ExportAlpha) == false || fileFormat == Bitmap::FileFormat::JpegFile) + if (is_set(exportFlags, ExportFlags::ExportAlpha) == false || fileFormat == Bitmap::FileFormat::JpegFile) { pImage = FreeImage_ConvertTo24Bits(pTemp); FreeImage_Unload(pTemp); @@ -435,7 +494,7 @@ namespace Falcor should_not_get_here(); } - if(warnings.empty() == false) + if (warnings.empty() == false) { logWarning("Bitmap::saveImage: " + joinStrings(warnings, " ")); } diff --git a/Framework/Source/Utils/Bitmap.h b/Source/Falcor/Utils/Image/Bitmap.h similarity index 94% rename from Framework/Source/Utils/Bitmap.h rename to Source/Falcor/Utils/Image/Bitmap.h index d9bb436d1..f7980c535 100644 --- a/Framework/Source/Utils/Bitmap.h +++ b/Source/Falcor/Utils/Image/Bitmap.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include namespace Falcor { @@ -34,7 +33,7 @@ namespace Falcor /** A class representing a memory bitmap */ - class Bitmap : public std::enable_shared_from_this + class dlldecl Bitmap : public std::enable_shared_from_this { public: enum class ExportFlags : uint32_t @@ -81,7 +80,7 @@ namespace Falcor \param[in] pTexture Texture to save to file */ - static void saveImageDialog(const std::shared_ptr& pTexture); + static void saveImageDialog(Texture* pTexture); ~Bitmap(); @@ -106,8 +105,9 @@ namespace Falcor */ static FileDialogFilterVec getFileDialogFilters(ResourceFormat format = ResourceFormat::Unknown); - - static std::string getFilExtFromResourceFormat(ResourceFormat format); + /** Get a file extension from a resource format + */ + static std::string getFileExtFromResourceFormat(ResourceFormat format); /** Get the file format flags for the image extension \param[in] ext The image file extension to get the diff --git a/Framework/Source/Utils/DDSHeader.h b/Source/Falcor/Utils/Image/DDSHeader.h similarity index 96% rename from Framework/Source/Utils/DDSHeader.h rename to Source/Falcor/Utils/Image/DDSHeader.h index 02e41cde9..4b5f3e21e 100644 --- a/Framework/Source/Utils/DDSHeader.h +++ b/Source/Falcor/Utils/Image/DDSHeader.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Utils/Platform/OS.h" -#include "Utils/DXHeader.h" +#include "Utils/Image/DXHeader.h" namespace Falcor { @@ -69,7 +68,7 @@ namespace Falcor uint32_t depth; uint32_t mipCount; uint32_t reserved[11]; - PixelFormat pixelFormat; + PixelFormat pixelFormat; uint32_t caps[4]; uint32_t reserved2; diff --git a/Framework/Source/Utils/DXHeader.cpp b/Source/Falcor/Utils/Image/DXHeader.cpp similarity index 94% rename from Framework/Source/Utils/DXHeader.cpp rename to Source/Falcor/Utils/Image/DXHeader.cpp index 116569dea..1ee1d7b9b 100644 --- a/Framework/Source/Utils/DXHeader.cpp +++ b/Source/Falcor/Utils/Image/DXHeader.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,19 +25,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" #ifdef _WIN32 -#include "Utils/DXHeader.h" -#include +#include "DXHeader.h" +#include namespace Falcor { #define assert_enum_value(prefix, val) static_assert(val == prefix##val, #val " enum value differs from D3D!") - assert_enum_value(D3D10_, RESOURCE_DIMENSION_UNKNOWN); - assert_enum_value(D3D10_, RESOURCE_DIMENSION_BUFFER); - assert_enum_value(D3D10_, RESOURCE_DIMENSION_TEXTURE1D); - assert_enum_value(D3D10_, RESOURCE_DIMENSION_TEXTURE2D); - assert_enum_value(D3D10_, RESOURCE_DIMENSION_TEXTURE3D); + assert_enum_value(D3D12_, RESOURCE_DIMENSION_UNKNOWN); + assert_enum_value(D3D12_, RESOURCE_DIMENSION_BUFFER); + assert_enum_value(D3D12_, RESOURCE_DIMENSION_TEXTURE1D); + assert_enum_value(D3D12_, RESOURCE_DIMENSION_TEXTURE2D); + assert_enum_value(D3D12_, RESOURCE_DIMENSION_TEXTURE3D); assert_enum_value(DXGI_, FORMAT_UNKNOWN); assert_enum_value(DXGI_, FORMAT_R32G32B32A32_TYPELESS); diff --git a/Framework/Source/Utils/DXHeader.h b/Source/Falcor/Utils/Image/DXHeader.h similarity index 99% rename from Framework/Source/Utils/DXHeader.h rename to Source/Falcor/Utils/Image/DXHeader.h index d09f02851..400cefa47 100644 --- a/Framework/Source/Utils/DXHeader.h +++ b/Source/Falcor/Utils/Image/DXHeader.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Utils/Logger.cpp b/Source/Falcor/Utils/Logger.cpp new file mode 100644 index 000000000..24ccd7b07 --- /dev/null +++ b/Source/Falcor/Utils/Logger.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Logger.h" + +namespace Falcor +{ + namespace + { + bool sShowErrorBox = true; + FILE* sLogFile = nullptr; + Logger::Level sVerbosity = Logger::Level::Warning; + + FILE* openLogFile() + { + FILE* pFile = nullptr; + + // Get current process name + std::string filename = getExecutableName(); + + // Now we have a folder and a filename, look for an available filename (we don't overwrite existing files) + std::string prefix = std::string(filename); + std::string executableDir = getExecutableDirectory(); + std::string logFile; + if (findAvailableFilename(prefix, executableDir, "log", logFile)) + { + pFile = std::fopen(logFile.c_str(), "w"); + if (pFile != nullptr) + { + // Success + return pFile; + } + } + // If we got here, we couldn't create a log file + should_not_get_here(); + return pFile; + } + + bool init() + { + bool b = false; +#if _LOG_ENABLED + sLogFile = openLogFile(); + b = sLogFile != nullptr; + assert(b); +#endif + return b; + } + + bool sInit = init(); + } + + void Logger::shutdown() + { +#if _LOG_ENABLED + if(sLogFile) + { + fclose(sLogFile); + sLogFile = nullptr; + sInit = false; + } +#endif + } + + const char* getLogLevelString(Logger::Level L) + { + const char* c = nullptr; +#define create_level_case(_l) case _l: c = "(" #_l ")" ;break; + switch(L) + { + create_level_case(Logger::Level::Info); + create_level_case(Logger::Level::Warning); + create_level_case(Logger::Level::Error); + default: + should_not_get_here(); + } +#undef create_level_case + return c; + } + + void Logger::log(Level L, const std::string& msg, MsgBox mbox) + { +#if _LOG_ENABLED + if(sInit) + { + if(L >= sVerbosity) + { + std::string s = getLogLevelString(L) + std::string("\t") + msg + "\n"; + std::fprintf(sLogFile, "%s", s.c_str()); + if (isDebuggerPresent()) + { + printToDebugWindow(s); + } + } + } +#endif + + bool showMsgBox = false; + + switch (mbox) + { + case MsgBox::Auto: + showMsgBox = (L >= Level::Error) ? sShowErrorBox : false; + break; + case MsgBox::Nope: + showMsgBox = false; + break; + case MsgBox::Show: + showMsgBox = true; + break; + default: + should_not_get_here(); + } + + if (showMsgBox) + { + static bool exiting = false; + bool quit = false; + if (exiting == false) + { + if (isDebuggerPresent()) + { + auto res = msgBox(msg + "\n\nPress Abort to quit, Retry to debug or Ignore to continue", MsgBoxType::AbortRetryIgnore); + if (res == MsgBoxButton::Retry) debugBreak(); + if (res == MsgBoxButton::Abort) quit = true; + } + else + { + quit = msgBox(msg + "\n\nContinue execution?", MsgBoxType::YesNo) == MsgBoxButton::No; + } + } + + if (quit) exit(1); // Don't post quit message. It will cause execution of the current frame to resume, which might crash the app + } + + if (L >= Level::Fatal) assert(false); // Assert on errors even without debugger attached + } + + void Logger::showBoxOnError(bool showBox) { sShowErrorBox = showBox; } + bool Logger::isBoxShownOnError() { return sShowErrorBox; } + void Logger::setVerbosity(Level level) { sVerbosity = level; } +} diff --git a/Framework/Source/Utils/Logger.h b/Source/Falcor/Utils/Logger.h similarity index 68% rename from Framework/Source/Utils/Logger.h rename to Source/Falcor/Utils/Logger.h index a2c6935c7..7446eec74 100644 --- a/Framework/Source/Utils/Logger.h +++ b/Source/Falcor/Utils/Logger.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,8 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include "FalcorConfig.h" namespace Falcor { @@ -35,7 +33,7 @@ namespace Falcor * To enable log messages, make sure _LOG_ENABLED is set to true in FalcorConfig.h. * Messages are printed to a log file in the application directory. Using Logger#ShowBoxOnError() you can control if a message box will be shown as well. */ - class Logger + class dlldecl Logger { public: /** Log messages severity @@ -49,10 +47,14 @@ namespace Falcor Disabled = -1 }; - /** Initialize logger and open log file. - \return Whether initialization was successful + /** Message box behavior */ - static bool initialize(); + enum class MsgBox + { + Auto, ///< Show a message box only if the verbosity is Error or higher **and** `isBoxShownOnError()` returns `true` + Show, ///< Show a message box + Nope ///< Don't show a message box + }; /** Shutdown the logger and close the log file. */ @@ -76,31 +78,18 @@ namespace Falcor */ static void setVerbosity(Level level); - struct Data - { -#ifdef _DEBUG - bool showErrorBox = true; -#else - bool showErrorBox = false; -#endif - FILE* pLogFile = nullptr; - bool initialized = false; - Level verbosity = Level::Warning; - }; - private: - friend void logInfo(const std::string& msg, bool forceMsgBox); - friend void logWarning(const std::string& msg, bool forceMsgBox); - friend void logError(const std::string& msg, bool forceMsgBox); - friend void logErrorAndExit(const std::string& msg, bool forceMsgBox); - - static void log(Level L, const std::string& msg, bool forceMsgBox = false); + friend void logInfo(const std::string& msg, MsgBox mbox); + friend void logWarning(const std::string& msg, MsgBox mbox); + friend void logError(const std::string& msg, MsgBox mbox); + friend void logErrorAndExit(const std::string& msg, MsgBox mbox); + static void log(Level L, const std::string& msg, MsgBox mbox = Logger::MsgBox::Auto); Logger() = delete; }; - inline void logInfo(const std::string& msg, bool forceMsgBox = false) { Logger::log(Logger::Level::Info, msg, forceMsgBox); } - inline void logWarning(const std::string& msg, bool forceMsgBox = false) { Logger::log(Logger::Level::Warning, msg, forceMsgBox); } - inline void logError(const std::string& msg, bool forceMsgBox = false) { Logger::log(Logger::Level::Error, msg, forceMsgBox); } - inline void logErrorAndExit(const std::string& msg, bool forceMsgBox = false) { Logger::log(Logger::Level::Error, msg + "\nTerminating...", forceMsgBox); exit(1); } + inline void logInfo(const std::string& msg, Logger::MsgBox mbox = Logger::MsgBox::Auto) { Logger::log(Logger::Level::Info, msg, mbox); } + inline void logWarning(const std::string& msg, Logger::MsgBox mbox = Logger::MsgBox::Auto) { Logger::log(Logger::Level::Warning, msg, mbox); } + inline void logError(const std::string& msg, Logger::MsgBox mbox = Logger::MsgBox::Auto) { Logger::log(Logger::Level::Error, msg, mbox); } + inline void logErrorAndExit(const std::string& msg, Logger::MsgBox mbox = Logger::MsgBox::Auto) { Logger::log(Logger::Level::Error, msg + "\nTerminating...", mbox); exit(1); } } diff --git a/Framework/Source/Utils/AABB.h b/Source/Falcor/Utils/Math/AABB.h similarity index 97% rename from Framework/Source/Utils/AABB.h rename to Source/Falcor/Utils/Math/AABB.h index f6685e4df..d55447310 100644 --- a/Framework/Source/Utils/AABB.h +++ b/Source/Falcor/Utils/Math/AABB.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,9 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec3.hpp" -#include "glm/mat4x4.hpp" -#include "glm/common.hpp" +#include "Vector.h" namespace Falcor { @@ -124,4 +122,4 @@ namespace Falcor return BoundingBox::fromMinMax(min(bb0.getMinPos(), bb1.getMinPos()), max(bb0.getMaxPos(), bb1.getMaxPos())); } }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Utils/Math/AABB.slang b/Source/Falcor/Utils/Math/AABB.slang new file mode 100644 index 000000000..48510fee2 --- /dev/null +++ b/Source/Falcor/Utils/Math/AABB.slang @@ -0,0 +1,169 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Utils/Math/MathConstants.slang" + +/** Axis-aligned bounding box (AABB). +*/ +struct AABB +{ + float3 minPoint; ///< Minimum point. + float3 maxPoint; ///< Maximum point. If any minPoint > maxPoint the box is invalid. + + /** Set box to single point. + */ + [mutating] void set(float3 p) + { + minPoint = p; + maxPoint = p; + } + + /** Set the box corners explicitly. + Note if min > max in any component the box is invalid. + */ + [mutating] void set(float3 _min, float3 _max) + { + minPoint = _min; + maxPoint = _max; + } + + /** Invalidates the box. + */ + [mutating] void invalidate() + { + minPoint = FLT_MAX; + maxPoint = -FLT_MAX; + } + + /** Returns true if the box is valid. + */ + bool valid() + { + return all(minPoint <= maxPoint); + } + + /** Grows the box to include the point p. + */ + [mutating] void include(float3 p) + { + minPoint = min(minPoint, p); + maxPoint = max(maxPoint, p); + } + + /** Grows the box to include another box. + */ + [mutating] void include(AABB b) + { + minPoint = min(minPoint, b.minPoint); + maxPoint = max(maxPoint, b.maxPoint); + } + + /** Check if point is included in the box. + \return True if p is in the box (inclusive test), false if outside or box invalid. + */ + bool contains(float3 p) + { + return valid() && all(p >= minPoint && p <= maxPoint); + } + + /** Returns the box center. + \return Center of the box if valid, undefined otherwise. + */ + float3 center() + { + return (minPoint + maxPoint) * 0.5f; + } + + /** Returns the box extent. + \return Size of the box if valid, undefined otherwise. + */ + float3 extent() + { + return maxPoint - minPoint; + } + + /** Returns the surface area of the box. + \return Surface area if box is valid, undefined otherwise. + */ + float area() + { + float3 e = extent(); + return (e.x * e.y + e.x * e.z + e.y * e.z) * 2.f; + } + + /** Return the volume of the box. + \return Volume if the box is valid, undefined otherwise. + */ + float volume() + { + float3 e = extent(); + return e.x * e.y * e.z; + } + + /** Returns the radius of the minimal sphere that encloses the box. + \return Radius of minimal bounding sphere, or undefined if box is invalid. + */ + float radius() + { + return 0.5f * length(extent()); + } + + /** Check if two boxes intersect. The test is inclusive on both sides. + \param[in] other The other box. + \return True if the two boxes intersect. The result is undefined if either box is invalid. + */ + bool intersects(const AABB other) + { + return all(maxPoint >= other.minPoint && minPoint <= other.maxPoint); + } + + /** Returns the minimum distance between the box and a point. + The distance is the minimum distance between any point in the box and the other point. + \param[in] p The point to which to compute the distance. + \return Minimum distance between between point and box, or 0 if the point lies inside. The result is undefined if the box is invalid. + */ + float minDistance(float3 p) + { + float3 d1 = minPoint - p; + float3 d2 = p - maxPoint; + float3 d = max(max(d1, d2), 0.f); + return length(d); + } + + /** Returns the minimum distance between two boxes. + The distance is the minimum distance between any two points in the boxes. + \param[in] other The other box. + \return Minimum distance between boxes, or 0 if they intersect. The result is undefined if either box is invalid. + */ + float minDistance(const AABB other) + { + float3 d1 = minPoint - other.maxPoint; + float3 d2 = other.minPoint - maxPoint; + float3 d = max(max(d1, d2), 0.f); + return length(d); + } +}; diff --git a/Source/Falcor/Utils/Math/BBox.h b/Source/Falcor/Utils/Math/BBox.h new file mode 100644 index 000000000..07043b396 --- /dev/null +++ b/Source/Falcor/Utils/Math/BBox.h @@ -0,0 +1,97 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Utils/Math/Vector.h" +#include + +namespace Falcor +{ + /** An axis-aligned bounding box stored by its min/max points. + The user is responsible for checking the validity of returned bounding-boxes before using them. + Note: Falcor already has an AABB class that works differently, hence the name. + */ + struct BBox + { + glm::float3 minPoint = glm::float3(std::numeric_limits::infinity()); // +inf + glm::float3 maxPoint = glm::float3(-std::numeric_limits::infinity()); // -inf + + BBox() {} + BBox(const glm::float3& p) : minPoint(p), maxPoint(p) {} + + /** Returns true if bounding box is valid (all dimensions zero or larger). */ + bool valid() const { return maxPoint.x >= minPoint.x && maxPoint.y >= minPoint.y && maxPoint.z >= minPoint.z; } + + /** Returns the dimensions of the bounding box. */ + glm::float3 dimensions() const { return maxPoint - minPoint; } + + /** Returns the centroid of the bounding box. */ + glm::float3 centroid() const { return (minPoint + maxPoint) * 0.5f; } + + /** Returns the surface area of the bounding box. */ + float surfaceArea() const + { + const glm::float3 dims = dimensions(); + return 2.0f * (dims.x * dims.y + dims.y * dims.z + dims.x * dims.z); + } + + /** Returns the volume of the bounding box. + \param[in] epsilon Replace dimensions that are zero by this value. + \return the volume of the bounding box if it is valid, -inf otherwise. + */ + float volume(float epsilon = 0.0f) const + { + if (valid() == false) + { + return -std::numeric_limits::infinity(); + } + + const glm::float3 dims = glm::max(glm::vec3(epsilon), dimensions()); + return dims.x * dims.y * dims.z; + } + + /** Union of two boxes. */ + BBox& operator|= (const BBox& rhs) + { + minPoint = glm::min(minPoint, rhs.minPoint); + maxPoint = glm::max(maxPoint, rhs.maxPoint); + return *this; + } + + BBox operator| (const BBox& rhs) const { BBox bb = *this; bb |= rhs; return bb; } + + /** Intersection of two boxes. */ + BBox& operator&= (const BBox& rhs) + { + minPoint = glm::max(minPoint, rhs.minPoint); + maxPoint = glm::min(maxPoint, rhs.maxPoint); + return *this; + } + + BBox operator& (const BBox& rhs) const { BBox bb = *this; bb &= rhs; return bb; } + }; +} diff --git a/Source/Falcor/Utils/Math/BitTricks.slang b/Source/Falcor/Utils/Math/BitTricks.slang new file mode 100644 index 000000000..a638735d4 --- /dev/null +++ b/Source/Falcor/Utils/Math/BitTricks.slang @@ -0,0 +1,120 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Utility functions for Morton codes. + This is using the usual bit twiddling. See e.g.: https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + + The interleave functions are named based to their output size in bits. + The deinterleave functions are named based on their input size in bits. + So, deinterleave_16bit(interleave_16bit(x)) == x should hold true. + + TODO: Make this a host/device shared header, ensure code compiles on the host. + TODO: Add optimized 8-bit and 2x8-bit interleaving functions. + TODO: Use NvApi intrinsics to optimize the code on NV. +*/ + +/** 32-bit bit interleave (Morton code). + \param[in] v 16-bit values in the LSBs of each component (higher bits don't matter). + \return 32-bit value. +*/ +uint interleave_32bit(uint2 v) +{ + uint x = v.x & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 + uint y = v.y & 0x0000ffff; + + x = (x | (x << 8)) & 0x00FF00FF; // x = ---- ---- fedc ba98 ---- ---- 7654 3210 + x = (x | (x << 4)) & 0x0F0F0F0F; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210 + x = (x | (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10 + x = (x | (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +/** 16-bit bit interleave (Morton code). + \param[in] v 8-bit values in the LSBs of each component (higher bits don't matter). + \return 16-bit value in the lower word, 0 elsewhere. +*/ +uint interleave_16bit(uint2 v) +{ + v &= 0xff; + uint j = (v.y << 16) | v.x; // j = ---- ---- ( y ) ---- ---- ( x ) + // j = ---- ---- fedc ba98 ---- ---- 7654 3210 + j = (j ^ (j << 4)) & 0x0f0f0f0f; // j = ---- fedc ---- ba98 ---- 7654 ---- 3210 + j = (j ^ (j << 2)) & 0x33333333; // j = --fe --dc --ba --98 --76 --54 --32 --10 + j = (j ^ (j << 1)) & 0x55555555; // j = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + return (j >> 15) | (j & 0xffff); // j = ---- ---- ---- ---- f7e6 d5c4 b3a2 9180 +} + +/** 16-bit bit de-interleave (inverse Morton code). + \param[in] i 16-bit value in lower word, must be 0 elsewhere. + \return 8-bit values in the LSBs of each component, 0 elsewhere. +*/ +uint2 deinterleave_16bit(uint i) +{ + uint j = ((i >> 1) << 16) | i; // j = -( i >> 1 ) ( i ) + j &= 0x55555555; // j = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + j = (j ^ (j >> 1)) & 0x33333333; // j = --fe --dc --ba --98 --76 --54 --32 --10 + j = (j ^ (j >> 2)) & 0x0f0f0f0f; // j = ---- fedc ---- ba98 ---- 7654 ---- 3210 + j = (j ^ (j >> 4)) & 0x00ff00ff; // j = ---- ---- fedc ba98 ---- ---- 7654 3210 + return uint2(j & 0xff, j >> 16); // x = ---- ---- ---- ---- ---- ---- 7654 3210 + // y = ---- ---- ---- ---- ---- ---- fedc ba98 +} + +/** 8-bit bit de-interleave (inverse Morton code). + Note: This function has almost exactly the same cost as deinterleave_2x8bit, use the latter if multiple values should be de-interleaved. + \param[in] i 8-bit value in lower word, must be 0 elsewhere. + \return 4-bit values in the LSBs of each component, 0 elsewhere. +*/ +uint2 deinterleave_8bit(uint i) +{ + uint j = ((i >> 1) << 8) | i; // j = ---- ---- ---- ---- -(i >> 1) ( i ) + j &= 0x00005555; // j = ---- ---- ---- ---- -7-6 -5-4 -3-2 -1-0 + j = (j ^ (j >> 1)) & 0x33333333; // j = ---- ---- ---- ---- --76 --54 --32 --10 + j = (j ^ (j >> 2)) & 0x0f0f0f0f; // j = ---- ---- ---- ---- ---- 7654 ---- 3210 + return uint2(j & 0xf, j >> 8); // x = ---- ---- ---- ---- ---- ---- ---- 3210 + // y = ---- ---- ---- ---- ---- ---- ---- 7654 +} + +/** 2x 8-bit bit de-interleave (inverse Morton code). + \param[in] i 8-bit values in the LSBs of each 16-bit word, must be 0 elsewhere. + \return 4-bit values in each component in the LSBs of each 16-bit word, 0 elsewhere. +*/ +uint2 deinterleave_2x8bit(uint i) +{ + uint j = ((i & ~0x00010001) << 7) | i; // j = -(i1 >> 1)( i1 ) -(i0 >> 1)( i0 ) + j &= 0x55555555; // j = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + j = (j ^ (j >> 1)) & 0x33333333; // j = --fe --dc --ba --98 --76 --54 --32 --10 + j = (j ^ (j >> 2)) & 0x0f0f0f0f; // j = ---- fedc ---- ba98 ---- 7654 ---- 3210 + return uint2(j, j >> 8) & 0x000f000f; // x = ---- ---- ---- ba98 ---- ---- ---- 3210 + // y = ---- ---- ---- fedc ---- ---- ---- 7654 +} diff --git a/Framework/Source/Utils/Math/CubicSpline.h b/Source/Falcor/Utils/Math/CubicSpline.h similarity index 99% rename from Framework/Source/Utils/Math/CubicSpline.h rename to Source/Falcor/Utils/Math/CubicSpline.h index db230734f..083981b74 100644 --- a/Framework/Source/Utils/Math/CubicSpline.h +++ b/Source/Falcor/Utils/Math/CubicSpline.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Framework/Source/Utils/Math/FalcorMath.h b/Source/Falcor/Utils/Math/FalcorMath.h similarity index 98% rename from Framework/Source/Utils/Math/FalcorMath.h rename to Source/Falcor/Utils/Math/FalcorMath.h index 2234de3f6..43848e0d2 100644 --- a/Framework/Source/Utils/Math/FalcorMath.h +++ b/Source/Falcor/Utils/Math/FalcorMath.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,7 +28,6 @@ #pragma once #include "glm/gtc/quaternion.hpp" #include "glm/geometric.hpp" -#include "glm/mat4x4.hpp" #include "glm/gtc/matrix_transform.hpp" #define _USE_MATH_DEFINES #include @@ -221,4 +220,4 @@ namespace Falcor #endif /*! @} */ -} \ No newline at end of file +} diff --git a/Framework/Source/VR/OpenVR/VRPlayArea.cpp b/Source/Falcor/Utils/Math/FormatConversion.slang similarity index 52% rename from Framework/Source/VR/OpenVR/VRPlayArea.cpp rename to Source/Falcor/Utils/Math/FormatConversion.slang index 38f915307..bdf8942ba 100644 --- a/Framework/Source/VR/OpenVR/VRPlayArea.cpp +++ b/Source/Falcor/Utils/Math/FormatConversion.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,50 +26,50 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "FalcorConfig.h" +/** Utility code for converting between various packed formats. -#include "VRSystem.h" -#include "openvr.h" -#include "VRController.h" -#include "VRTrackerBox.h" -#include "VRPlayArea.h" + The functions have been written to be compatible with the DXGI formats. + Some use the 'precise' keyword to ensure bit exact results. We should add + unit tests to make sure it is correctly implemented. -using namespace Falcor; + It'd also be good to add optimized versions that don't care about NaN/inf + propagation etc., as well as make the header shareable between the CPU/GPU. +*/ +int floatToSnorm16(float v) +{ + v = isnan(v) ? 0.f : min(max(v, -1.f), 1.f); + return (int)trunc(v * 32767.f + (v >= 0.f ? 0.5f : -0.5f)); +} -VRPlayArea::SharedPtr VRPlayArea::create( vr::IVRSystem *vrSys, vr::IVRChaperone *chaperone ) +/** Unpack a single 16-bit snorm from the lower bits of a dword. +*/ +float unpackSnorm16(uint packed) { - SharedPtr play = SharedPtr( new VRPlayArea ); - play->mpChaperone = chaperone; - play->mpVrSys = vrSys; - return play; + int bits = (int)(packed << 16) >> 16; + precise float unpacked = max((float)bits / 32767.f, -1.0f); + return unpacked; } -bool VRPlayArea::isCalibrated( void ) const +/** Pack single float into a 16-bit snorm in the lower bits of the returned dword. +*/ +uint packSnorm16(precise float v) { - if ( !mpChaperone ) return false; - if ( mpChaperone->GetCalibrationState() != vr::ChaperoneCalibrationState_OK ) - return false; - return true; + return floatToSnorm16(v) & 0x0000ffff; } -bool VRPlayArea::isPartiallyCalibrated( void ) const +/** Unpack two 16-bit snorm values from the lo/hi bits of a dword. +*/ +float2 unpackSnorm2x16(uint packed) { - if ( !mpChaperone ) return false; - vr::ChaperoneCalibrationState state = mpChaperone->GetCalibrationState(); - if ( state < vr::ChaperoneCalibrationState_Error ) - return true; - return false; + int2 bits = int2(packed << 16, packed) >> 16; + precise float2 unpacked = max((float2)bits / 32767.f, -1.0f); + return unpacked; } -glm::vec2 VRPlayArea::getSize( void ) const +/** Pack two floats into 16-bit snorm values in the lo/hi bits of a dword. +*/ +uint packSnorm2x16(precise float2 v) { - float hasData = false; - float x, z; - if ( mpChaperone ) - { - hasData = mpChaperone->GetPlayAreaSize( &x, &z ); - } - // 0.5 so that the room goes from [-x...x] x [0...2.5] x [-y...y] - return hasData ? glm::vec2( 0.5*x, 0.5*z ) : glm::vec2( 0 ); + return (floatToSnorm16(v.x) & 0x0000ffff) | (floatToSnorm16(v.y) << 16); } diff --git a/Source/Falcor/Utils/Math/HalfUtils.slang b/Source/Falcor/Utils/Math/HalfUtils.slang new file mode 100644 index 000000000..4af1fff52 --- /dev/null +++ b/Source/Falcor/Utils/Math/HalfUtils.slang @@ -0,0 +1,74 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Utility functions for fp16 math. +*/ + +/** Converts a finite fp32 number to fp16, rounding down to the nearest representable number. + If the result does not fit in the range of fp16, +-inf is returned, assuming the input was finite. + \return Rounded fp16 value in the low 16-bits. The high 16-bits are zero. +*/ +uint f32tof16_roundDown(float value) +{ + uint h = f32tof16(value); + float res = f16tof32(h); // TODO: Use precise keyword when available. + if (res > value) + { + // Result was rounded up. + // Depending on the value, the next smaller fp16 number is given by: + // res < 0: +1 gives the next smaller + // res > 0: -1 gives the next smaller + // res == +-0: next smaller is 0x8001 + if (res < 0.f) h++; + else if (res > 0.f) h--; + else h = 0x8001; + } + return h; +} + +/** Converts a finite fp32 number to fp16, rounding up to the nearest representable number. + If the result does not fit in the range of fp16, +-inf is returned, assuming the input was finite. + \return Rounded fp16 value in the low 16-bits. The high 16-bits are zero. +*/ +uint f32tof16_roundUp(float value) +{ + uint h = f32tof16(value); + float res = f16tof32(h); // TODO: Use precise keyword when available + if (res < value) + { + // Result was rounded down. + // Depending on the value, the next larger fp16 number is given by: + // res < 0: -1 gives the next larger + // res > 0: +1 gives the next larger + // res == +-0: next larger is 0x0001 + if (res < 0.f) h--; + else if (res > 0.f) h++; + else h = 0x0001; + } + return h; +} diff --git a/Source/Falcor/Utils/Math/HashUtils.slang b/Source/Falcor/Utils/Math/HashUtils.slang new file mode 100644 index 000000000..cecff1dea --- /dev/null +++ b/Source/Falcor/Utils/Math/HashUtils.slang @@ -0,0 +1,71 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** This file contains various hash functions and other utilities + for pseudorandom number generation. +*/ + +/** 32-bit (non-cryptographic) hash function by Robert Jenkins. + This is a perfect hash function (no collisions). + See https://gist.github.com/badboy/6267743. +*/ +uint jenkinsHash(uint a) +{ + a = (a + 0x7ed55d16) + (a << 12); + a = (a ^ 0xc761c23c) ^ (a >> 19); + a = (a + 0x165667b1) + (a << 5); + a = (a + 0xd3a2646c) ^ (a << 9); + a = (a + 0xfd7046c5) + (a << 3); + a = (a ^ 0xb55a4f09) ^ (a >> 16); + return a; +} + +/** Generates a pair of 32-bit pseudorandom numbers based on a pair of 32-bit values. + + The code uses a 64-bit block cipher, the Tiny Encryption Algorithm (TEA) by Wheeler et al., 1994. + The 128-bit key is fixed and adapted from here: https://www.ibiblio.org/e-notes/webcl/mc.htm. + This function can be useful for seeding other pseudorandom number generators. + + \param[in] v0 The first value (low dword of the block). + \param[in] v1 The second value (high dword of the block). + \param[in] iterations Number of iterations (the authors recommend 16 at a minimum). + \return Two pseudorandom numbers (the block cipher of (v0,v1)). +*/ +uint2 blockCipherTEA(uint v0, uint v1, uint iterations = 16) +{ + uint sum = 0; + const uint delta = 0x9e3779b9; + const uint k[4] = { 0xa341316c, 0xc8013ea4, 0xad90777d, 0x7e95761e }; // 128-bit key. + for (uint i = 0; i < iterations; i++) + { + sum += delta; + v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]); + v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]); + } + return uint2(v0, v1); +} diff --git a/Source/Falcor/Utils/Math/MathConstants.slang b/Source/Falcor/Utils/Math/MathConstants.slang new file mode 100644 index 000000000..58676edc3 --- /dev/null +++ b/Source/Falcor/Utils/Math/MathConstants.slang @@ -0,0 +1,107 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +/** This file contains useful numeric constants. + + It should be included with #include rather than import, as imported files + do not export macro definitions. + + Note the possible differences between declaring constants using static + float vs macro definitions. The compiler may treat these differently with + respect to what precision is used for compile-time constant propagation. + Slang currently uses fp32 for constant propagation. We get higher + precision using the pre-evaluated constants below. Ideally, all + compile-time constants should be evaluated at fp64 or higher precision. +*/ + +// Constants from +#define M_E 2.71828182845904523536 // e +#define M_LOG2E 1.44269504088896340736 // log2(e) +#define M_LOG10E 0.434294481903251827651 // log10(e) +#define M_LN2 0.693147180559945309417 // ln(2) +#define M_LN10 2.30258509299404568402 // ln(10) +#define M_PI 3.14159265358979323846 // pi +#define M_PI_2 1.57079632679489661923 // pi/2 +#define M_PI_4 0.785398163397448309616 // pi/4 +#define M_1_PI 0.318309886183790671538 // 1/pi +#define M_2_PI 0.636619772367581343076 // 2/pi +#define M_2_SQRTPI 1.12837916709551257390 // 2/sqrt(pi) +#define M_SQRT2 1.41421356237309504880 // sqrt(2) +#define M_SQRT1_2 0.707106781186547524401 // 1/sqrt(2) + +// Additional constants +#define M_2PI 6.28318530717958647693 // 2pi +#define M_4PI 12.5663706143591729539 // 4pi +#define M_4_PI 1.27323954473516268615 // 4/pi +#define M_1_2PI 0.159154943091895335769 // 1/2pi +#define M_1_4PI 0.079577471545947667884 // 1/4pi +#define M_SQRTPI 1.77245385090551602730 // sqrt(pi) +#define M_1_SQRT2 0.707106781186547524401 // 1/sqrt(2) + +// Numeric limits from +#define DBL_DECIMAL_DIG 17 // # of decimal digits of rounding precision +#define DBL_DIG 15 // # of decimal digits of precision +#define DBL_EPSILON 2.2204460492503131e-016 // smallest such that 1.0+DBL_EPSILON != 1.0 +#define DBL_HAS_SUBNORM 1 // type does support subnormal numbers +#define DBL_MANT_DIG 53 // # of bits in mantissa +#define DBL_MAX 1.7976931348623158e+308 // max value +#define DBL_MAX_10_EXP 308 // max decimal exponent +#define DBL_MAX_EXP 1024 // max binary exponent +#define DBL_MIN 2.2250738585072014e-308 // min positive value +#define DBL_MIN_10_EXP (-307) // min decimal exponent +#define DBL_MIN_EXP (-1021) // min binary exponent +#define DBL_RADIX 2 // exponent radix +#define DBL_TRUE_MIN 4.9406564584124654e-324 // min positive value + +#define FLT_DECIMAL_DIG 9 // # of decimal digits of rounding precision +#define FLT_DIG 6 // # of decimal digits of precision +#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0 +#define FLT_HAS_SUBNORM 1 // type does support subnormal numbers +#define FLT_GUARD 0 +#define FLT_MANT_DIG 24 // # of bits in mantissa +#define FLT_MAX 3.402823466e+38F // max value +#define FLT_MAX_10_EXP 38 // max decimal exponent +#define FLT_MAX_EXP 128 // max binary exponent +#define FLT_MIN 1.175494351e-38F // min normalized positive value +#define FLT_MIN_10_EXP (-37) // min decimal exponent +#define FLT_MIN_EXP (-125) // min binary exponent +#define FLT_NORMALIZE 0 +#define FLT_RADIX 2 // exponent radix +#define FLT_TRUE_MIN 1.401298464e-45F // min positive value + +// Numeric limits for half (IEEE754 binary16) +#define HLF_EPSILON 9.765625e-04F // smallest such that 1.0+HLF_EPSILON != 1.0 +#define HLF_HAS_SUBNORM 1 // type does support subnormal numbers +#define HLF_MANT_DIG 11 +#define HLF_MAX 6.5504e+4F // max value +#define HLF_MAX_EXP 16 // max binary exponent +#define HLF_MIN 6.097555160522461e-05F // min normalized positive value +#define HLF_MIN_EXP (-14) // min binary exponent +#define HLF_RADIX 2 +#define HLF_TRUE_MIN 5.960464477539063e-08F // min positive value diff --git a/Source/Falcor/Utils/Math/MathHelpers.slang b/Source/Falcor/Utils/Math/MathHelpers.slang new file mode 100644 index 000000000..1d93cd512 --- /dev/null +++ b/Source/Falcor/Utils/Math/MathHelpers.slang @@ -0,0 +1,572 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** This file contains various math utility helper functions. + + Included functionality (in order): + + - Sherical coordinates mapping functions + - Octahedral mapping functions + - Sampling functions (disk, sphere, triangle etc.) + - Misc stuff (matrix inversion, bounding cones etc.) + +*/ + +// Include math constants (M_PI etc.). These are for use in this file only, +// as macro definitions are not exported from a Slang module. +#include "Utils/Math/MathConstants.slang" + +/****************************************************************************** + + Spherical coordinates + + Functions for converting Cartesian coordinates to spherical coordinates + using standard mathematical notations. + + The latitude-longitude map uses (phi,theta) as positions in two dimensions. + Its using using other conventions to orient and wrap the map the same way + as in common 3D software (e.g. Autodesk Maya). + +******************************************************************************/ + +/** Converts Cartesian coordinates to spherical coordinates (unsigned normalized). + 'theta' is the polar angle (inclination) between the +z axis and the vector from origin to p, normalized to [0,1]. + 'phi' is the azimuthal angle from the +x axis in the xy-plane, normalized to [0,1]. + \param[in] p Cartesian coordinates (x,y,z). + \return Spherical coordinates (theta,phi). +*/ +float2 cartesian_to_spherical_unorm(float3 p) +{ + p = normalize(p); + float2 sph; + sph.x = acos(p.z) * M_1_PI; + sph.y = atan2(-p.y, -p.x) * M_1_2PI + 0.5f; + return sph; +} + +/** Converts Cartesian coordinates to spherical coordinates (radians). + 'theta' is the polar angle (inclination) between the +z axis and the vector from origin to p, in the range [0,pi]. + 'phi' is the azimuthal angle from the +x axis in the xy-plane, in the range [0,2pi]. + \param[in] p Cartesian coordinates (x,y,z). + \return Spherical coordinates (theta,phi). +*/ +float2 cartesian_to_spherical_rad(float3 p) +{ + p = normalize(p); + float2 sph; + sph.x = acos(p.z); + sph.y = atan2(-p.y, -p.x) + M_PI; + return sph; +} + +/** Convert world space direction to (u,v) coord in latitude-longitude map (unsigned normalized). + The map is centered around the -z axis and wrapping around in clockwise order (left to right). + \param[in] dir World space direction (unnormalized). + \return Position in latitude-longitude map in [0,1] for each component. +*/ +float2 world_to_latlong_map(float3 dir) +{ + float3 p = normalize(dir); + float2 uv; + uv.x = atan2(p.x, -p.z) * M_1_2PI + 0.5f; + uv.y = acos(p.y) * M_1_PI; + return uv; +} + +/****************************************************************************** + + Octahedral mapping + + The center of the map represents the +z axis and its corners -z. + The rotated inner square is the xy-plane projected onto the upper hemi- + sphere, the outer four triangles folds down over the lower hemisphere. + There are versions for equal-area and non-equal area (slightly faster). + + For details refer to: + - Clarberg 2008, "Fast Equal-Area Mapping of the (Hemi)Sphere using SIMD". + - Cigolle et al. 2014, "Survey of Efficient Representations for Independent Unit Vectors". + +******************************************************************************/ + +/** Helper function to reflect the folds of the lower hemisphere + over the diagonals in the octahedral map. +*/ +float2 oct_wrap(float2 v) +{ + return (1.f - abs(v.yx)) * (v.xy >= 0.f ? 1.f : -1.f); +} + +/** Converts normalized direction to the octahedral map (non-equal area, signed normalized). + \param[in] n Normalized direction. + \return Position in octahedral map in [-1,1] for each component. +*/ +float2 ndir_to_oct_snorm(float3 n) +{ + // Project the sphere onto the octahedron (|x|+|y|+|z| = 1) and then onto the xy-plane. + float2 p = n.xy * (1.f / (abs(n.x) + abs(n.y) + abs(n.z))); + p = (n.z < 0.f) ? oct_wrap(p) : p; + return p; +} + +/** Converts normalized direction to the octahedral map (non-equal area, unsigned normalized). + \param[in] n Normalized direction. + \return Position in octahedral map in [0,1] for each component. +*/ +float2 ndir_to_oct_unorm(float3 n) +{ + return ndir_to_oct_snorm(n) * 0.5f + 0.5f; +} + +/** Converts point in the octahedral map to normalized direction (non-equal area, signed normalized). + \param[in] p Position in octahedral map in [-1,1] for each component. + \return Normalized direction. +*/ +float3 oct_to_ndir_snorm(float2 p) +{ + float3 n = float3(p.xy, 1.0 - abs(p.x) - abs(p.y)); + n.xy = (n.z < 0.0) ? oct_wrap(n.xy) : n.xy; + return normalize(n); +} + +/** Converts point in the octahedral map to normalized direction (non-equal area, unsigned normalized). + \param[in] p Position in octahedral map in [0,1] for each component. + \return Normalized direction. +*/ +float3 oct_to_ndir_unorm(float2 p) +{ + return oct_to_ndir_snorm(p * 2.f - 1.f); +} + +/** Converts normalized direction to the octahedral map (equal-area, unsigned normalized). + \param[in] n Normalized direction. + \return Position in octahedral map in [0,1] for each component. +*/ +float2 ndir_to_oct_equal_area_unorm(float3 n) +{ + // Use atan2 to avoid explicit div-by-zero check in atan(y/x). + float r = sqrt(1.f - abs(n.z)); + float phi = atan2(abs(n.y), abs(n.x)); + + // Compute p = (u,v) in the first quadrant. + float2 p; + p.y = r * phi * M_2_PI; + p.x = r - p.y; + + // Reflect p over the diagonals, and move to the correct quadrant. + if (n.z < 0.f) p = 1.f - p.yx; + p *= sign(n.xy); + + return p * 0.5f + 0.5f; +} + +/** Converts point in the octahedral map to normalized direction (equal area, unsigned normalized). + \param[in] p Position in octahedral map in [0,1] for each component. + \return Normalized direction. +*/ +float3 oct_to_ndir_equal_area_unorm(float2 p) +{ + p = p * 2.f - 1.f; + + // Compute radius r without branching. The radius r=0 at +z (center) and at -z (corners). + float d = 1.f - (abs(p.x) + abs(p.y)); + float r = 1.f - abs(d); + + // Compute phi in [0,pi/2] (first quadrant) and sin/cos without branching. + // TODO: Analyze fp32 precision, do we need a small epsilon instead of 0.0 here? + float phi = (r > 0.f) ? ((abs(p.y) - abs(p.x)) / r + 1.f) * M_PI_4 : 0.f; + + // Convert to Cartesian coordinates. Note that sign(x)=0 for x=0, but that's fine here. + float f = r * sqrt(2.f - r*r); + float x = f * sign(p.x) * cos(phi); + float y = f * sign(p.y) * sin(phi); + float z = sign(d) * (1.f - r*r); + + return float3(x, y, z); +} + +/****************************************************************************** + + Sampling functions + +******************************************************************************/ + +/** Uniform sampling of the unit disk using polar coordinates. + \param[in] u Uniform random number in [0,1)^2. + \return Sampled point on the unit disk. +*/ +float2 sample_disk(float2 u) +{ + float2 p; + float r = sqrt(u.x); + float phi = M_2PI * u.y; + p.x = r * cos(phi); + p.y = r * sin(phi); + return p; +} + +/** Uniform sampling of the unit sphere using spherical coordinates. + \param[in] u Uniform random numbers in [0,1)^2. + \return Sampled point on the unit sphere. +*/ +float3 sample_sphere(float2 u) +{ + float phi = M_2PI * u.y; + float cosTheta = 1.0f - 2.0f * u.x; + float sinTheta = sqrt(max(0.0f, 1.0f - cosTheta * cosTheta)); + return float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); +} + +/** Uniform sampling of the unit disk using Shirley's concentric mapping. + \param[in] u Uniform random numbers in [0,1)^2. + \return Sampled point on the unit disk. +*/ +float2 sample_disk_concentric(float2 u) +{ + u = 2.f * u - 1.f; + if (u.x == 0.f && u.y == 0.f) return u; + float phi, r; + if (abs(u.x) > abs(u.y)) + { + r = u.x; + phi = (u.y / u.x) * M_PI_4; + } + else + { + r = u.y; + phi = M_PI_2 - (u.x / u.y) * M_PI_4; + } + return r * float2(cos(phi), sin(phi)); +} + +/** Cosine-weighted sampling of the hemisphere using Shirley's concentric mapping. + \param[in] u Uniform random numbers in [0,1)^2. + \param[out] pdf Probability density of the sampled direction (= cos(theta)/pi). + \return Sampled direction in the local frame (+z axis up). +*/ +float3 sample_cosine_hemisphere_concentric(float2 u, out float pdf) +{ + float2 d = sample_disk_concentric(u); + float z = sqrt(max(0.f, 1.f - dot(d, d))); + pdf = z * M_1_PI; + return float3(d, z); +} + +/** Cosine-weighted sampling of the hemisphere using a polar coordinates. + \param[in] u Uniform random numbers in [0,1)^2. + \param[out] pdf Probability density of the sampled direction (= cos(theta)/pi). + \return Sampled direction in the local frame (+z axis up). +*/ +float3 sample_cosine_hemisphere_polar(float2 u, out float pdf) +{ + float3 p; + float r = sqrt(u.x); + float phi = M_2PI * u.y; + p.x = r * cos(phi); + p.y = r * sin(phi); + p.z = sqrt(1.f - u.x); + pdf = p.z * M_1_PI; + return p; +} + +/** Cosine-weighted sampling of the hemisphere using a polar coordinates. + This overload does not compute the pdf for the generated sample. + \param[in] u Uniform random numbers in [0,1)^2. + \return Sampled direction in the local frame (+z axis up). +*/ +float3 sample_cosine_hemisphere_polar(float2 u) +{ + float pdf; + return sample_cosine_hemisphere_polar(u, pdf); +} + +/** Uniform sampling of a triangle. + \param[in] u Uniform random numbers in [0,1)^2. + \return Barycentric coordinates (1-u-v,u,v) of the sampled point. +*/ +float3 sample_triangle(float2 u) +{ + float su = sqrt(u.x); + float2 b = float2(1.f - su, u.y * su); + return float3(1.f - b.x - b.y, b.x, b.y); +} + +/****************************************************************************** + + Miscellaneous functions + +******************************************************************************/ + +/** Inverts a 3x3 matrix. +*/ +float3x3 inverse(float3x3 M) +{ + float3x3 inv; + float invdet = 1.0f / determinant(M); + inv[0][0] = (M[1][1] * M[2][2] - M[2][1] * M[1][2]) * invdet; + inv[0][1] = (M[0][2] * M[2][1] - M[0][1] * M[2][2]) * invdet; + inv[0][2] = (M[0][1] * M[1][2] - M[0][2] * M[1][1]) * invdet; + inv[1][0] = (M[1][2] * M[2][0] - M[1][0] * M[2][2]) * invdet; + inv[1][1] = (M[0][0] * M[2][2] - M[0][2] * M[2][0]) * invdet; + inv[1][2] = (M[1][0] * M[0][2] - M[0][0] * M[1][2]) * invdet; + inv[2][0] = (M[1][0] * M[2][1] - M[2][0] * M[1][1]) * invdet; + inv[2][1] = (M[2][0] * M[0][1] - M[0][0] * M[2][1]) * invdet; + inv[2][2] = (M[0][0] * M[1][1] - M[1][0] * M[0][1]) * invdet; + return inv; +} + +/** Gernerate a vector that is orthogonal to the input vector. + This can be used to invent a tangent frame for meshes that don't have real tangents/bitangents. +*/ +float3 perp_stark(float3 u) +{ + // TODO: Validate this and look at numerical precision etc. Are there better ways to do it? + float3 a = abs(u); + uint uyx = (a.x - a.y) < 0 ? 1 : 0; + uint uzx = (a.x - a.z) < 0 ? 1 : 0; + uint uzy = (a.y - a.z) < 0 ? 1 : 0; + uint xm = uyx & uzx; + uint ym = (1 ^ xm) & uzy; + uint zm = 1 ^ (xm | ym); // 1 ^ (xm & ym) + float3 v = cross(u, float3(xm, ym, zm)); + return v; +} + +/** Computes the cosine of the half angle of the minimum bounding cone that encloses an AABB, as seen from a particular viewpoint. + We use an optimized algorithm that exploits the symmetry around the plane perpendicular to the central direction. + + \param[in] origin Viewpoint origin. + \param[in] aabbMin minimum corner of the AABB. + \param[in] aabbMax maximum corner of the AABB. + \param[out] coneDir normalized vector defining the cone's axis. + \param[out] sinTheta Sine of the angle from the central direction to the cone edge. If the AABB can't be bounded we return 0. + \param[out] cosTheta Cosine of the angle from the central direction to the cone edge. If the AABB can't be bounded we return -1 (max cone). +*/ +void boundBoxSubtendedConeAngleCenter(const float3 origin, const float3 aabbMin, const float3 aabbMax, + out float3 coneDir, out float sinTheta, out float cosTheta) +{ + const float3 center = (aabbMax + aabbMin) * 0.5f; + const float3 extent = (aabbMax - aabbMin) * 0.5f; + const float3 dir = center - origin; // dir = Central cone direction (unnormalized) + const float extSqr = dot(extent, extent); // extSqr = squared maximum extent + const float distSqr = dot(dir, dir); // distSqr = squared distance to AABB center + + coneDir = normalize(dir); + + // AABB has eight corners, located at p = center +- e[i] for i=1..4. + // We use the absolute value of the dot product below to avoid having to test all eight. + float3 e[4]; + e[0] = float3(extent.x, extent.y, extent.z); + e[1] = float3(extent.x, extent.y, -extent.z); + e[2] = float3(extent.x, -extent.y, extent.z); + e[3] = float3(extent.x, -extent.y, -extent.z); + + cosTheta = 1.f; + sinTheta = 0.f; + + // Workaround slang/fxc bug (https://github.com/NVIDIAGameWorks/Falcor/issues/164). This can go away when we always use dxc. +#if 0 + [unroll] + for (uint i = 0; i < 4; i++) +#else + $for(i in Range(0,4)) +#endif + { + // Compute distance x from origin to corner projected onto central axis. + // Note that x is scaled by |dir| since we use unnormalized vectors. + float d = abs(dot(dir, e[i])); + float x = distSqr - d; + + // Check if distance is negative, in which case the AABB can't be bounded by a cone and we return a cone that covers the whole sphere (theta = pi). + if (x < 1e-5) + { + cosTheta = -1.f; + sinTheta = 0.f; + return; // TODO: Look at numerical precision. + } + + // Compute distance y from the corner to the projection on the central axis (also scaled by |dir|). + float y = sqrt(max(0, distSqr * extSqr - d * d)); // TODO: Look at numerical precision. Clamp for now just to be extra safe. + + // Compute hypotenuse of the triangle. + float z = sqrt(x * x + y * y); + + // The cosine of the cone angle cos(theta) = x / z. Track the minimum cosTheta, since we want + // cos(theta) for the maximum theta. + cosTheta = min(cosTheta, x / z); + // And along similar lines for sin(theta). + sinTheta = max(sinTheta, y / z); + } +} + +/** Computes the solid angle subtended by an AABB by first computing the + average vector to all of its vertices and then finding the maximum + angle between that and each of the vectors to its vertices. + + \param[in] origin point from which the solid angle is being comupted. + \param[in] aabbMin minimum corner of the AABB. + \param[in] aabbMax maximum corner of the AABB. + \param[out] coneDir central cone direction (normalized) or null vector if origin is inside the AABB. + \param[out] sinTheta sine of the angle. + \param[out] cosTheta cosine of the angle. +*/ +void boundBoxSubtendedConeAngleAverage(float3 origin, float3 aabbMin, float3 aabbMax, + out float3 coneDir, out float sinTheta, out float cosTheta) +{ + if (all(origin >= aabbMin && origin <= aabbMax)) + { + // |origin| is inside the AABB. + coneDir = float3(0.f, 0.f, 0.f); + sinTheta = 0.f; + cosTheta = -1.f; + return; + } + + // Compute the average vector to each of the bounding box corners. + float3 dirSum = float3(0.f, 0.f, 0.f); + // Workaround slang/fxc bug (https://github.com/NVIDIAGameWorks/Falcor/issues/164). This can go away when we always use dxc. +#if 0 + [unroll] + for (int i = 0; i < 8; ++i) +#else + $for(i in Range(0,8)) +#endif + { + const float3 corner = float3((i & 1) ? aabbMin.x : aabbMax.x, + (i & 2) ? aabbMin.y : aabbMax.y, + (i & 4) ? aabbMin.z : aabbMax.z); + dirSum += normalize(corner - origin); + } + coneDir = normalize(dirSum); + + // Compute the cosine of the maximum angle between a corner and the + // average vector. + cosTheta = 1.f; +#if 0 + [unroll] + for (int i = 0; i < 8; ++i) +#else + $for(i in Range(0,8)) +#endif + { + const float3 corner = float3((i & 1) ? aabbMin.x : aabbMax.x, + (i & 2) ? aabbMin.y : aabbMax.y, + (i & 4) ? aabbMin.z : aabbMax.z); + cosTheta = min(cosTheta, dot(normalize(corner - origin), coneDir)); + } + sinTheta = sqrt(max(0.f, 1.f - cosTheta * cosTheta)); +} + +/** Computes the sine and cosine of the angle of the cone that encompasses + a sphere of given radius and center subtend as seen from a point at the + origin. + + \param[in] center Sphere's center. + \param[in] radius Sphere's radius. + \param[out] sinTheta sine of the angle between a vector from the origin to |center| and a + vector from the origin that is tangent to the sphere. + \param[out] cosTheta cosine of that angle. +*/ +void boundSphereSubtendedConeAngle(float3 center, float radius, out float sinTheta, out float cosTheta) +{ + const float centerDistance2 = dot(center, center); + // Is the point inside the bounding sphere? + if (centerDistance2 < radius * radius) + { + sinTheta = 0.f; + cosTheta = -1.f; + } + else + { + // Compute the sine and then the cosine of the spread angle of a + // cone that bounds the sphere as seen from |center|. + const float sin2Theta = radius * radius / centerDistance2; + cosTheta = sqrt(max(0.f, 1.f - sin2Theta)); + sinTheta = sqrt(sin2Theta); + } +} + +/** Computes the squared minimum distance between a point and a triangle. + This function is not sensitive to the handedness of the coordinate system. + \param[in] vertices Positions of the three vertices. + \param[in] p Coordinates of the point. + \return Squared minimum distance between p and the triangle. +*/ +float computeSquaredMinDistanceToTriangle(const float3 vertices[3], const float3 p) +{ + // Project p onto the plane of the triangle (the result is independent of triangle winding). + const float3 n = normalize(cross(vertices[1] - vertices[0], vertices[2] - vertices[0])); + const float projDistance = dot(n, (p - vertices[0])); + const float3 pProj = p - projDistance * n; + + // Edge tests to compute signed distance to each edge. + // Positive result means the projected point is "inside" the edge. + // With flipped winding, the edges are flipped but n is also flipped so it still works. + const float3 edges[3] = { + normalize(vertices[1] - vertices[0]), + normalize(vertices[2] - vertices[1]), + normalize(vertices[0] - vertices[2]) + }; + float sqrPlanarDistance = FLT_MAX; + uint insideMask = 0u; + [unroll] + for (uint i = 0u; i < 3u; ++i) + { + const float3 edgeN = cross(n, edges[i]); + const float edgeProjDistance = dot(edgeN, pProj - vertices[i]); + if (edgeProjDistance >= 0.0f) + { + insideMask |= 1u << i; + } + else + { + const float3 vec = pProj - vertices[i]; + sqrPlanarDistance = min(edgeProjDistance * edgeProjDistance, sqrPlanarDistance); + } + } + if (insideMask == 0x7) + { + sqrPlanarDistance = 0.0f; + } + + // If only one edge is considering the point as inside, then the projected point + // is closest to a triangle corner (the vertex opposite of that edge). + else if (insideMask == 1u << 0u) + { + sqrPlanarDistance = dot(pProj - vertices[2], pProj - vertices[2]); + } + else if (insideMask == 1u << 1u) + { + sqrPlanarDistance = dot(pProj - vertices[0], pProj - vertices[0]); + } + else if (insideMask == 1u << 2u) + { + sqrPlanarDistance = dot(pProj - vertices[1], pProj - vertices[1]); + } + + return projDistance * projDistance + sqrPlanarDistance; +} diff --git a/Samples/Core/ShaderBuffers/Data/ShaderBuffers.hlsl b/Source/Falcor/Utils/Math/PackedFormats.slang similarity index 59% rename from Samples/Core/ShaderBuffers/Data/ShaderBuffers.hlsl rename to Source/Falcor/Utils/Math/PackedFormats.slang index c5632806c..f72d28960 100644 --- a/Samples/Core/ShaderBuffers/Data/ShaderBuffers.hlsl +++ b/Source/Falcor/Utils/Math/PackedFormats.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,49 +25,41 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "VertexAttrib.h" +import Utils.Math.MathHelpers; +import Utils.Math.FormatConversion; -cbuffer PerFrameCB +/** Encode a normal packed as 2x 16-bit snorms in the octahedral mapping. +*/ +uint encodeNormal2x16(float3 normal) { - float4x4 gWorldMat; - float4x4 gWvpMat; -}; - -struct LightCB -{ - float3 vec3Val; // We're using 2 values. [0]: worldDir [1]: intensity -}; - -RWStructuredBuffer gRWBuffer; // Only UAV counter used -StructuredBuffer gLight[4]; -RWByteAddressBuffer gInvocationBuffer; -Buffer gSurfaceColor[2]; + float2 octNormal = ndir_to_oct_snorm(normal); + return packSnorm2x16(octNormal); +} -struct VsOut +/** Decode a normal packed as 2x 16-bit snorms in the octahedral mapping. +*/ +float3 decodeNormal2x16(uint packedNormal) { - float4 position : SV_POSITION; - float3 normalW : NORMAL; -}; + float2 octNormal = unpackSnorm2x16(packedNormal); + return oct_to_ndir_snorm(octNormal); +} -float4 ps(VsOut vsOut) : SV_TARGET +/** Encode a normal packed as 3x 16-bit snorms. Note: The high 16 bits of the second dword are unused. +*/ +uint2 encodeNormal3x16(float3 normal) { - float3 n = normalize(vsOut.normalW); - float nDotL = dot(n, -gLight[3][0].vec3Val); - nDotL = clamp(nDotL, 0, 1); - float4 color = float4(nDotL * gLight[3][1].vec3Val * gSurfaceColor[1][0], 1); - - gInvocationBuffer.InterlockedAdd(0, 1); - gRWBuffer.IncrementCounter(); - - return color; + uint2 packedNormal; + packedNormal.x = packSnorm2x16(normal.xy); + packedNormal.y = packSnorm16(normal.z); + return packedNormal; } -VsOut vs(in float4 posL : POSITION, in float3 normalL : NORMAL) +/** Decode a normal packed as 3x 16-bit snorms. Note: The high 16 bits of the second dword are unused. +*/ +float3 decodeNormal3x16(uint2 packedNormal) { - VsOut vsOut; - - vsOut.position = mul(posL, gWvpMat); - vsOut.normalW = (mul(float4(normalL, 0), gWorldMat)).xyz; - - return vsOut; + float3 normal; + normal.xy = unpackSnorm2x16(packedNormal.x); + normal.z = unpackSnorm16(packedNormal.y); + return normalize(normal); } diff --git a/Source/Falcor/Utils/Math/SphericalHarmonics.slang b/Source/Falcor/Utils/Math/SphericalHarmonics.slang new file mode 100644 index 000000000..926d90d39 --- /dev/null +++ b/Source/Falcor/Utils/Math/SphericalHarmonics.slang @@ -0,0 +1,85 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Utils.Math.MathHelpers; + + +/** Get sequential index of SH basis function of degree l>=0 and order m in [-l,l]. +*/ +uint get_SH_index(int l, int m) +{ + // There are l^2 basis functions of degree 0..l-1. And we add +l to shift order to positive values. + return (uint)(l * (l + 1) + m); +} + +/** Evaluates the spherical harmonics basis function Y_i at Cartesian coordinate p=(x,y,z) on the unit sphere. + \param[in] idx Sequential SH basis function index, where 0 <= idx < 16 (SH degree 3 and lower). + \param[in] p Cartesian coordinate p=(x,y,z) on the unit sphere. + \return Evaluated SH basis function. +*/ +float eval_SH(uint idx, float3 p) +{ + // Standard real SH basis. See https://en.wikipedia.org/wiki/Table_of_spherical_harmonics + // Note that in Appendix A2 of Sloan 2008, "Stupid Spherical Harmonics (SH) Tricks", + // the signs are reversed for basis functions with odd m. We're not using Sloan's definitions. + // TODO: More general implementation that supports higher degrees. + switch (idx) + { + // l = 0 + case 0: return 1.f / (2.f * M_SQRT_PIf); // m = 0 + // l = 1 + case 1: return p.y * sqrt(3.f) / (2.f * M_SQRT_PIf); // m =-1 + case 2: return p.z * sqrt(3.f) / (2.f * M_SQRT_PIf); // m = 0 + case 3: return p.x * sqrt(3.f) / (2.f * M_SQRT_PIf); // m = 1 + // l = 2 + case 4: return p.x * p.y * sqrt(15.f) / (2.f * M_SQRT_PIf); // m =-2 + case 5: return p.y * p.z * sqrt(15.f) / (2.f * M_SQRT_PIf); // m =-1 + case 6: return (3.f * p.z * p.z - 1.f) * sqrt(5.f) / (4.f * M_SQRT_PIf); // m = 0 + case 7: return p.x * p.z * sqrt(15.f) / (2.f * M_SQRT_PIf); // m = 1 + case 8: return (p.x * p.x - p.y * p.y) * sqrt(15.f) / (4.f * M_SQRT_PIf); // m = 2 + // l = 3 + case 9: return p.y * (3.f * p.x * p.x - p.y * p.y) * sqrt(70.f) / (8.f * M_SQRT_PIf); // m =-3 + case 10: return p.x * p.y * p.z * sqrt(105.f) / (2.f * M_SQRT_PIf); // m =-2 + case 11: return p.y * (5.f * p.z * p.z - 1.f) * sqrt(42.f) / (8.f * M_SQRT_PIf); // m =-1 + case 12: return p.z * (5.f * p.z * p.z - 3.f) * sqrt(7.f) / (4.f * M_SQRT_PIf); // m = 0 + case 13: return p.x * (5.f * p.z * p.z - 1.f) * sqrt(42.f) / (8.f * M_SQRT_PIf); // m = 1 + case 14: return p.z * (p.x * p.x - p.y * p.y) * sqrt(105.f) / (4.f * M_SQRT_PIf); // m = 2 + case 15: return p.x * (p.x * p.x - 3.f * p.y * p.y) * sqrt(70.f) / (8.f * M_SQRT_PIf); // m = 3 + } + return 0.f; +} + +/** Evaluates the spherical harmonics basis function Y_l^m at Cartesian coordinate p=(x,y,z) on the unit sphere. + \param[in] l SH degree 0 <= l < 3. + \param[in] m SH order m in [-l,l]. + \param[in] p Cartesian coordinate p=(x,y,z) on the unit sphere. + \return Evaluated SH basis function. +*/ +float eval_SH(int l, int m, float3 p) +{ + return eval_SH(get_SH_index(l, m), p); +} diff --git a/Source/Falcor/Utils/Math/Vector.h b/Source/Falcor/Utils/Math/Vector.h new file mode 100644 index 000000000..d95f2c0de --- /dev/null +++ b/Source/Falcor/Utils/Math/Vector.h @@ -0,0 +1,37 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#define GLM_FORCE_CTOR_INIT +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/glm.hpp" +#include "glm/gtx/compatibility.hpp" + +namespace Falcor +{ + using namespace glm; +} diff --git a/Framework/Source/Utils/Psychophysics/Experiment.cpp b/Source/Falcor/Utils/Perception/Experiment.cpp similarity index 98% rename from Framework/Source/Utils/Psychophysics/Experiment.cpp rename to Source/Falcor/Utils/Perception/Experiment.cpp index 03e57c5b9..f146a5029 100644 --- a/Framework/Source/Utils/Psychophysics/Experiment.cpp +++ b/Source/Falcor/Utils/Perception/Experiment.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,13 +25,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" #include "Experiment.h" -#include #include +#include namespace Falcor { - namespace Psychophysics + namespace Perception { void Experiment::describeExperiment(ExperimentDescription newExpDesc) { @@ -202,4 +203,4 @@ namespace Falcor } } -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Psychophysics/Experiment.h b/Source/Falcor/Utils/Perception/Experiment.h similarity index 97% rename from Framework/Source/Utils/Psychophysics/Experiment.h rename to Source/Falcor/Utils/Perception/Experiment.h index 28de4b4a5..5cd6e2fba 100644 --- a/Framework/Source/Utils/Psychophysics/Experiment.h +++ b/Source/Falcor/Utils/Perception/Experiment.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,13 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - -#include "SingleThresholdMeasurement.h" #include +#include "SingleThresholdMeasurement.h" namespace Falcor { - namespace Psychophysics + namespace Perception { /** Description of an experiment: Any information that could be useful in future, in case an experiment grows while piloting or when @@ -47,7 +46,7 @@ namespace Falcor /** A class representing a psychophysical experiment */ - class Experiment + class dlldecl Experiment { public: @@ -109,4 +108,4 @@ namespace Falcor ExperimentDescription mExpDesc; }; } -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Psychophysics/SingleThresholdMeasurement.cpp b/Source/Falcor/Utils/Perception/SingleThresholdMeasurement.cpp similarity index 93% rename from Framework/Source/Utils/Psychophysics/SingleThresholdMeasurement.cpp rename to Source/Falcor/Utils/Perception/SingleThresholdMeasurement.cpp index 60ce47941..08597bc2a 100644 --- a/Framework/Source/Utils/Psychophysics/SingleThresholdMeasurement.cpp +++ b/Source/Falcor/Utils/Perception/SingleThresholdMeasurement.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,11 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ +#include "stdafx.h" #include "SingleThresholdMeasurement.h" namespace Falcor { - namespace Psychophysics + namespace Perception { void SingleThresholdMeasurement::initMeasurement(ConditionParameter initConditionParam, ExperimentalDesignParameter initExpParam) // return true if successfully initialized, false if not { @@ -41,7 +42,7 @@ namespace Falcor { mExpParam = initExpParam; mConditionParam = initConditionParam; - if (mExpParam.mMeasuringMethod == DiscreteStaircase) + if (mExpParam.mMeasuringMethod == Method::DiscreteStaircase) { if (mExpParam.mIsDefault) // only mMinLevel, mMaxLevel, mMinLevelStepSize were defined { @@ -85,7 +86,7 @@ namespace Falcor mReversalCount = 0; mLimitHitCount = 0; } - else if (mExpParam.mMeasuringMethod == BucketStaircase) // SC with pre-determined stimLevels + else if (mExpParam.mMeasuringMethod == Method::BucketStaircase) // SC with pre-determined stimLevels { if (mExpParam.mIsDefault) // only stimLevels were defined { @@ -128,7 +129,7 @@ namespace Falcor mReversalCount = 0; mLimitHitCount = 0; } - else if (mExpParam.mMeasuringMethod == MethodOfConstantStimuli) // MCS + else if (mExpParam.mMeasuringMethod == Method::MethodOfConstantStimuli) // MCS { if (mExpParam.mIsDefault) // only stimLevels were defined { @@ -166,14 +167,14 @@ namespace Falcor Response res; res.mStimLevel = mCurrentLevel; res.mResponse = response; - if ((mExpParam.mMeasuringMethod == DiscreteStaircase) || (mExpParam.mMeasuringMethod == BucketStaircase)) // SC. This is for debugging. + if ((mExpParam.mMeasuringMethod == Method::DiscreteStaircase) || (mExpParam.mMeasuringMethod == Method::BucketStaircase)) // SC. This is for debugging. { res.mReversalCount = mReversalCount; } mResponses.push_back(res); // select next stim level based on measuring strategy (SC or MCS) - if (mExpParam.mMeasuringMethod == DiscreteStaircase) // SC. Count reversals and select next stim level. + if (mExpParam.mMeasuringMethod == Method::DiscreteStaircase) // SC. Count reversals and select next stim level. { if (mResponses.back().mResponse == 0) // incorrect response { @@ -247,7 +248,7 @@ namespace Falcor } } - else if (mExpParam.mMeasuringMethod == BucketStaircase) // SC with pre-determined stimLevels + else if (mExpParam.mMeasuringMethod == Method::BucketStaircase) // SC with pre-determined stimLevels { if (mResponses.back().mResponse == 0) // incorrect response { @@ -322,7 +323,7 @@ namespace Falcor std::cout << "Processed a response that was correct. Reversal count is: " << mReversalCount << "\n"; } } - else if (mExpParam.mMeasuringMethod == MethodOfConstantStimuli) // MCS. Count numTrials and select next stim level. + else if (mExpParam.mMeasuringMethod == Method::MethodOfConstantStimuli) // MCS. Count numTrials and select next stim level. { // count number of trials & calculate progress ratio per each stimuli level std::vector progressRatio; @@ -359,7 +360,7 @@ namespace Falcor float SingleThresholdMeasurement::getProgressRatio() { - if ((mExpParam.mMeasuringMethod == DiscreteStaircase) || (mExpParam.mMeasuringMethod == BucketStaircase)) // SC + if ((mExpParam.mMeasuringMethod == Method::DiscreteStaircase) || (mExpParam.mMeasuringMethod == Method::BucketStaircase)) // SC { // if maximum trial count reached, report 1 if ((int)mResponses.size() >= mExpParam.mMaxTotalTrialCount) @@ -371,7 +372,7 @@ namespace Falcor return (float)mReversalCount / (float)mExpParam.mMaxReversals; } } - else if (mExpParam.mMeasuringMethod == MethodOfConstantStimuli) // MCS + else if (mExpParam.mMeasuringMethod == Method::MethodOfConstantStimuli) // MCS { int32_t totalCount = 0; int32_t totalMax = 0; @@ -387,7 +388,7 @@ namespace Falcor bool SingleThresholdMeasurement::isComplete() { - if ((mExpParam.mMeasuringMethod == DiscreteStaircase) || (mExpParam.mMeasuringMethod == BucketStaircase)) // SC + if ((mExpParam.mMeasuringMethod == Method::DiscreteStaircase) || (mExpParam.mMeasuringMethod == Method::BucketStaircase)) // SC { if ((mReversalCount >= mExpParam.mMaxReversals) || ((int32_t)mResponses.size() >= mExpParam.mMaxTotalTrialCount)) { @@ -398,7 +399,7 @@ namespace Falcor return false; } } - else if (mExpParam.mMeasuringMethod == MethodOfConstantStimuli) // MCS + else if (mExpParam.mMeasuringMethod == Method::MethodOfConstantStimuli) // MCS { int32_t totalCount = 0; int32_t totalMax = 0; @@ -420,4 +421,4 @@ namespace Falcor } } -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Psychophysics/SingleThresholdMeasurement.h b/Source/Falcor/Utils/Perception/SingleThresholdMeasurement.h similarity index 90% rename from Framework/Source/Utils/Psychophysics/SingleThresholdMeasurement.h rename to Source/Falcor/Utils/Perception/SingleThresholdMeasurement.h index 1c04e1e43..e28cf266f 100644 --- a/Framework/Source/Utils/Psychophysics/SingleThresholdMeasurement.h +++ b/Source/Falcor/Utils/Perception/SingleThresholdMeasurement.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,27 +26,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - -#include -#include -#include #include -#include namespace Falcor { - namespace Psychophysics + namespace Perception { /** Psychophysics method types */ - enum PsychophysicsMethod { DiscreteStaircase, BucketStaircase, MethodOfConstantStimuli }; + enum class Method + { + DiscreteStaircase, + BucketStaircase, + MethodOfConstantStimuli + }; /** Struct for experimental design parameters: Contains all parameters for both Staircase and Method of Constant Stimuli. */ struct ExperimentalDesignParameter { - PsychophysicsMethod mMeasuringMethod; // 0 = General staircase, 1 = Staircase with pre-determined stimLevels, 2 = Method of Constant Stimuli + Method mMeasuringMethod; // 0 = General staircase, 1 = Staircase with pre-determined stimLevels, 2 = Method of Constant Stimuli bool mIsDefault; float mInitLevel, mInitLevelRandomRange, mMinLevel, mMaxLevel, mInitLevelStepSize, mMinLevelStepSize; // for SC int32_t mNumUp, mNumDown, mMaxReversals, mMaxTotalTrialCount, mMaxLimitHitCount; // for SC @@ -73,7 +73,7 @@ namespace Falcor /** Class to abstract single threshold measurement */ - class SingleThresholdMeasurement + class dlldecl SingleThresholdMeasurement { public: diff --git a/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h b/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h new file mode 100644 index 000000000..f3eea1f1e --- /dev/null +++ b/Source/Falcor/Utils/SampleGenerators/CPUSampleGenerator.h @@ -0,0 +1,57 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +namespace Falcor +{ + /** Two-dimensional sample pattern generator on the CPU. + */ + class dlldecl CPUSampleGenerator : public std::enable_shared_from_this + { + public: + using SharedPtr = std::shared_ptr; + virtual ~CPUSampleGenerator() = default; + + /** Return the total number of samples in the sample pattern. + */ + virtual uint32_t getSampleCount() const = 0; + + /** Reset the sample generator. + \param[in] startID Start at this sample ID in the sample pattern. + */ + virtual void reset(uint32_t startID = 0) = 0; + + /** Return the next two-dimensional sample. + \return Sample in the range [-0.5, 0.5) in each dimension. + */ + virtual glm::vec2 next() = 0; + + protected: + CPUSampleGenerator() = default; + }; +} diff --git a/Framework/Source/Utils/PatternGenerators/DxSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp similarity index 88% rename from Framework/Source/Utils/PatternGenerators/DxSamplePattern.cpp rename to Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp index 7503a6e96..813089d3c 100644 --- a/Framework/Source/Utils/PatternGenerators/DxSamplePattern.cpp +++ b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.cpp @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "DxSamplePattern.h" namespace Falcor @@ -38,4 +38,10 @@ namespace Falcor { -7.0f / 16.0f, -1.0f / 16.0f }, { 3.0f / 16.0f, 7.0f / 16.0f }, { 7.0f / 16.0f, -7.0f / 16.0f } }; -} \ No newline at end of file + + DxSamplePattern::DxSamplePattern(uint32_t sampleCount) + { + // FIXME: Support other sample counts + if (sampleCount != kSampleCount) logWarning("DxSamplePattern() currently requires sampleCount = 8. Using that number."); + } +} diff --git a/Framework/Source/Utils/PatternGenerators/DxSamplePattern.h b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h similarity index 77% rename from Framework/Source/Utils/PatternGenerators/DxSamplePattern.h rename to Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h index 68d369be5..a606d7c86 100644 --- a/Framework/Source/Utils/PatternGenerators/DxSamplePattern.h +++ b/Source/Falcor/Utils/SampleGenerators/DxSamplePattern.h @@ -26,31 +26,34 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "PatternGenerator.h" +#include "CPUSampleGenerator.h" namespace Falcor { - class DxSamplePattern : public PatternGenerator, public inherit_shared_from_this + /** Sample pattern generator for the Direct3D 8x MSAA/SSAA pattern. + */ + class dlldecl DxSamplePattern : public CPUSampleGenerator, public inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; virtual ~DxSamplePattern() = default; - static SharedPtr create() { return SharedPtr(new DxSamplePattern()); } + static SharedPtr create(uint32_t sampleCount = 8) { return SharedPtr(new DxSamplePattern(sampleCount)); } virtual uint32_t getSampleCount() const override { return kSampleCount; } - virtual void reset(uint32_t startID = 0) { mCurSample = 0; } + virtual void reset(uint32_t startID = 0) override { mCurSample = 0; } - virtual vec2 next() + virtual vec2 next() override { return kPattern[(mCurSample++) % kSampleCount]; } protected: - DxSamplePattern() = default; + DxSamplePattern(uint32_t sampleCount); uint32_t mCurSample = 0; static const uint32_t kSampleCount = 8; static const vec2 kPattern[kSampleCount]; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/PatternGenerators/HaltonSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp similarity index 83% rename from Framework/Source/Utils/PatternGenerators/HaltonSamplePattern.cpp rename to Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp index 42bade035..9cbee5b47 100644 --- a/Framework/Source/Utils/PatternGenerators/HaltonSamplePattern.cpp +++ b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.cpp @@ -25,7 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "HaltonSamplePattern.h" namespace Falcor @@ -38,4 +38,11 @@ namespace Falcor { 3.0f / 8.0f - 0.5f, 2.0f / 9.0f - 0.5f }, { 7.0f / 8.0f - 0.5f, 5.0f / 9.0f - 0.5f }, { 0.5f / 8.0f - 0.5f, 8.0f / 9.0f - 0.5f } }; -} \ No newline at end of file + + HaltonSamplePattern::HaltonSamplePattern(uint32_t sampleCount) + { + // FIXME: Support arbitrary sample counts by computing the sequence instead of using a table + if (sampleCount < 1 || sampleCount > 8) logWarning("HaltonSamplePattern() requires sampleCount in the range [1,8]. Clamping to that range."); + mSampleCount = std::max(1u, std::min(8u, sampleCount)); + } +} diff --git a/Framework/Source/Utils/PatternGenerators/HaltonSamplePattern.h b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h similarity index 82% rename from Framework/Source/Utils/PatternGenerators/HaltonSamplePattern.h rename to Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h index 38a6abec9..73e2ec928 100644 --- a/Framework/Source/Utils/PatternGenerators/HaltonSamplePattern.h +++ b/Source/Falcor/Utils/SampleGenerators/HaltonSamplePattern.h @@ -26,34 +26,32 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "PatternGenerator.h" +#include "CPUSampleGenerator.h" namespace Falcor { - class HaltonSamplePattern : public PatternGenerator, public inherit_shared_from_this + class dlldecl HaltonSamplePattern : public CPUSampleGenerator, public inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; virtual ~HaltonSamplePattern() = default; static SharedPtr create(uint32_t sampleCount = 8) { return SharedPtr(new HaltonSamplePattern(sampleCount)); } virtual uint32_t getSampleCount() const override { return mSampleCount; } - virtual void reset(uint32_t startID = 0) { mCurSample = 0; } + virtual void reset(uint32_t startID = 0) override { mCurSample = 0; } - virtual vec2 next() + virtual vec2 next() override { return kPattern[(mCurSample++) % mSampleCount]; } protected: - HaltonSamplePattern(uint32_t sampleCount) : mSampleCount(sampleCount) - { - assert(sampleCount == 8); - } + HaltonSamplePattern(uint32_t sampleCount); uint32_t mCurSample = 0; - const uint32_t mSampleCount = 8; + uint32_t mSampleCount = 0; static const vec2 kPattern[]; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp new file mode 100644 index 000000000..5492ba53d --- /dev/null +++ b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "StratifiedSamplePattern.h" + +namespace Falcor +{ + StratifiedSamplePattern::SharedPtr StratifiedSamplePattern::create(uint32_t sampleCount) + { + return SharedPtr(new StratifiedSamplePattern(sampleCount)); + } + + StratifiedSamplePattern::StratifiedSamplePattern(uint32_t sampleCount) + { + // Clamp sampleCount to a reasonable number so the permutation table doesn't get too big. + if (sampleCount < 1) logWarning("StratifiedSamplePattern() requires sampleCount > 0. Using one sample."); + else if (sampleCount > 1024) logWarning("StratifiedSamplePattern() requires sampleCount <= 1024. Using 1024 samples."); + sampleCount = std::clamp(sampleCount, 1u, 1024u); + + // Factorize sampleCount into an M x N grid, where M and N are as close as possible. + // In the worst case sampleCount is prime and we'll end up with a sampleCount x 1 grid. + mBinsX = (uint32_t)std::sqrt((double)sampleCount); + mBinsY = sampleCount / mBinsX; + while (mBinsX * mBinsY != sampleCount) + { + mBinsX++; + mBinsY = sampleCount / mBinsX; + } + assert(mBinsX * mBinsY == sampleCount); + + // Create permutation table. + mPermutation.resize(sampleCount); + for (uint32_t i = 0; i < sampleCount; i++) mPermutation[i] = i; + } + + void StratifiedSamplePattern::reset(uint32_t startID) + { + if (startID > 0) logWarning("StratifiedSamplePattern::reset() doesn't support restarting at an arbitrary sample. Using startID = 0."); + mCurSample = 0; + mRng = std::mt19937(); + } + + glm::vec2 StratifiedSamplePattern::next() + { + auto dist = std::uniform_real_distribution(); + auto u = [&]() { return dist(mRng); }; + + // Create new permutation at the start of each round of sampling. + if (mCurSample == 0) std::shuffle(mPermutation.begin(), mPermutation.end(), mRng); + + // Compute stratified point in the current bin. + uint32_t binIdx = mPermutation[mCurSample]; + uint32_t i = binIdx % mBinsX; + uint32_t j = binIdx / mBinsX; + mCurSample = (mCurSample + 1) % getSampleCount(); + + assert(i < mBinsX && j < mBinsY); + float x = ((float)i + u()) / mBinsX; + float y = ((float)j + u()) / mBinsY; + return glm::vec2(x, y) - 0.5f; + } +} diff --git a/Framework/Source/Graphics/Model/Loaders/ModelImporter.h b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h similarity index 52% rename from Framework/Source/Graphics/Model/Loaders/ModelImporter.h rename to Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h index faf9e06c8..eb831132d 100644 --- a/Framework/Source/Graphics/Model/Loaders/ModelImporter.h +++ b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,28 +25,45 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - #pragma once - -#include -#include "Graphics/Material/Material.h" +#include "CPUSampleGenerator.h" +#include namespace Falcor { - /** Base class for Model importer implementations. Stores common functionality and data. + /** Stratified random sample pattern generator. + + The number of samples is determined at creation time, but note that + the sample generator keeps generating random samples indefinitely. + The distribution is therefore uniform random after each multiple of + getSampleCount() samples. + + The order in which samples are generated is randomly permuted to avoid + correlation artefacts with low-discrepancy sample generators. */ - class ModelImporter + class dlldecl StratifiedSamplePattern : public CPUSampleGenerator, public inherit_shared_from_this { - protected: + public: + using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; + virtual ~StratifiedSamplePattern() = default; - // If a similar material already exists, will return the existing one. Otherwise, will cache the material in pMaterial and return it - /** Handles caching of materials while importing. If pMaterial is new, it will be cached and returned. - Otherwise, if a material with equivalent properties has been loaded, the cached material will be returned instead. - \param[in] pMaterial Material to check - \return If pMaterial has been cached, return the cached material instance. Otherwise return pMaterial. + /** Create stratified random sample pattern generator. + \param[in] sampleCount The number of sampling bins to stratify over. */ - Material::SharedPtr checkForExistingMaterial(const Material::SharedPtr& pMaterial); + static SharedPtr create(uint32_t sampleCount = 1); + + virtual uint32_t getSampleCount() const override { return mBinsX * mBinsY; } + virtual void reset(uint32_t startID = 0) override; + virtual vec2 next() override; + + protected: + StratifiedSamplePattern(uint32_t sampleCount); - std::vector mLoadedMaterials; // vector because we make use of operator==, and it's only for the importers + uint32_t mBinsX = 0; + uint32_t mBinsY = 0; + uint32_t mCurSample = 0; + std::mt19937 mRng; + std::vector mPermutation; }; } diff --git a/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang new file mode 100644 index 000000000..d1c6055ba --- /dev/null +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/LCG.slang @@ -0,0 +1,64 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Simple linear congruential generator (LCG). + + The code uses the parameters from the book series "Numerical Recipes". + The period is 2^32 and its state size is 32 bits. + + Note: Only for basic applications. The generator has poor statistical + properties and is sensitive to good seeding. If many parallel generators + are used (e.g. one per pixel) there will be significant correlation + between the generated pseudorandom sequences. In those cases, it is + recommended to use one of the generators with larger state. +*/ + +struct LCG +{ + uint state; +}; + +/** Generates the next pseudorandom number in the sequence (32 bits). +*/ +uint nextRandom(inout LCG rng) +{ + const uint A = 1664525u; + const uint C = 1013904223u; + rng.state = (A * rng.state + C); + return rng.state; +} + +/** Initialize LCG pseudorandom number generator. + \param[in] s0 Initial state (seed). +*/ +LCG createLCG(uint s0) +{ + LCG rng; + rng.state = s0; + return rng; +} diff --git a/Samples/Effects/Shadows/Data/Shadows.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang similarity index 51% rename from Samples/Effects/Shadows/Data/Shadows.slang rename to Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang index d18f282c4..67896ac04 100644 --- a/Samples/Effects/Shadows/Data/Shadows.slang +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/SplitMix64.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,50 +25,54 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import DefaultVS; -__import Shading; -__import ShaderCommon; -cbuffer PerFrameCB : register(b0) -{ - float4x4 camVpAtLastCsmUpdate; - bool visualizeCascades; - Texture2D gVisibilityBuffers[_LIGHT_COUNT]; -}; +/** SplitMix64 pseudorandom number generator. + + This is a fixed-increment version of Java 8's SplittableRandom generator. + The period is 2^64 and its state size is 64 bits. + It is a very fast generator passing BigCrush. It is recommended for use with + other generators like xoroshiro and xorshift to initialize their state arrays. + + Steele Jr, Guy L., Doug Lea, and Christine H. Flood., "Fast Splittable Pseudorandom Number Generators", + ACM SIGPLAN Notices 49.10 (2014): 453-472. http://dx.doi.org/10.1145/2714064.2660195. + + This code requires shader model 6.0 or above for 64-bit integer support. +*/ -struct ShadowsVSOut +struct SplitMix64 { - VertexOut vsData; - float shadowsDepthC : DEPTH; + uint64_t state; }; -ShadowsVSOut vsMain(VertexIn vIn) +uint64_t asuint64(uint lowbits, uint highbits) { - VertexOut defaultOut = defaultVS(vIn); - ShadowsVSOut output; - output.vsData = defaultOut; - - output.shadowsDepthC = mul(float4(defaultOut.posW, 1), camVpAtLastCsmUpdate).z; - return output; + return (uint64_t(highbits) << 32) | uint64_t(lowbits); } -float4 psMain(ShadowsVSOut pIn) : SV_TARGET0 +/** Generates the next pseudorandom number in the sequence (64 bits). +*/ +uint64_t nextRandom64(inout SplitMix64 rng) { - ShadingData sd = prepareShadingData(pIn.vsData, gMaterial, gCamera.posW); - float4 color = float4(0,0,0,1); - - [unroll] - for(uint l = 0 ; l < _LIGHT_COUNT ; l++) - { - float shadowFactor = gVisibilityBuffers[l].Load(int3(pIn.vsData.posH.xy, 0)).r; - color.rgb += evalMaterial(sd, gLights[l], shadowFactor).color.rgb; - } + uint64_t z = (rng.state += 0x9E3779B97F4A7C15ull); + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBull; + return z ^ (z >> 31); +} - if(visualizeCascades) - { - float3 cascadeColor = gVisibilityBuffers[_LIGHT_INDEX].Load(int3(pIn.vsData.posH.xy, 0)).gba; - color.rgb *= cascadeColor; - } +/** Generates the next pseudorandom number in the sequence (low 32 bits). +*/ +uint nextRandom(inout SplitMix64 rng) +{ + return (uint)nextRandom64(rng); +} - return color; +/** Initialize SplitMix64 pseudorandom number generator. + \param[in] s0 Low bits of initial state (seed). + \param[in] s1 High bits of initial state (seed). +*/ +SplitMix64 createSplitMix64(uint s0, uint s1) +{ + SplitMix64 rng; + rng.state = asuint64(s0, s1); + return rng; } diff --git a/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang b/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang new file mode 100644 index 000000000..5237d7b3c --- /dev/null +++ b/Source/Falcor/Utils/Sampling/Pseudorandom/Xoshiro.slang @@ -0,0 +1,114 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Implementation of the xoshiro128** 32-bit all-purpose, rock-solid generator + written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org). + The state is 128 bits and the period (2^128)-1. It has a jump function that + allows you to skip ahead 2^64 in the seqeuence. + + Note: The state must be seeded so that it is not everywhere zero. + The recommendation is to initialize the state using SplitMix64. + + See the original public domain code: http://xoshiro.di.unimi.it/xoshiro128starstar.c +*/ + +struct Xoshiro128StarStar +{ + uint state[4]; +}; + +uint rotl(const uint x, int k) +{ + return (x << k) | (x >> (32 - k)); +} + +/** Generates the next pseudorandom number in the sequence (32 bits). +*/ +uint nextRandom(inout Xoshiro128StarStar rng) +{ + const uint32_t result_starstar = rotl(rng.state[0] * 5, 7) * 9; + const uint32_t t = rng.state[1] << 9; + + rng.state[2] ^= rng.state[0]; + rng.state[3] ^= rng.state[1]; + rng.state[1] ^= rng.state[2]; + rng.state[0] ^= rng.state[3]; + + rng.state[2] ^= t; + rng.state[3] = rotl(rng.state[3], 11); + + return result_starstar; +} + +/** Jump function for the generator. It is equivalent to 2^64 calls to nextRandom(). + It can be used to generate 2^64 non-overlapping subsequences for parallel computations. +*/ +void jump(inout Xoshiro128StarStar rng) +{ + static const uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }; + + uint32_t s0 = 0; + uint32_t s1 = 0; + uint32_t s2 = 0; + uint32_t s3 = 0; + + for (int i = 0; i < 4; i++) + { + for (int b = 0; b < 32; b++) + { + if (JUMP[i] & (1u << b)) + { + s0 ^= rng.state[0]; + s1 ^= rng.state[1]; + s2 ^= rng.state[2]; + s3 ^= rng.state[3]; + } + nextRandom(rng); + } + } + + rng.state[0] = s0; + rng.state[1] = s1; + rng.state[2] = s2; + rng.state[3] = s3; +} + +/** Initialize Xoshiro128StarStar pseudorandom number generator. + The initial state should be pseudorandom and must not be zero everywhere. + It is recommended to use SplitMix64 for creating the initial state. + \param[in] s Array of 4x 32-bit values of initial state (seed). +*/ +Xoshiro128StarStar createXoshiro128StarStar(uint s[4]) +{ + Xoshiro128StarStar rng; + rng.state[0] = s[0]; + rng.state[1] = s[1]; + rng.state[2] = s[2]; + rng.state[3] = s[3]; + return rng; +} diff --git a/Framework/Source/Graphics/Model/Loaders/ModelImporter.cpp b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp similarity index 72% rename from Framework/Source/Graphics/Model/Loaders/ModelImporter.cpp rename to Source/Falcor/Utils/Sampling/SampleGenerator.cpp index a1f4d5fb9..443431a62 100644 --- a/Framework/Source/Graphics/Model/Loaders/ModelImporter.cpp +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,25 +25,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Graphics/Model/Loaders/ModelImporter.h" +#include "stdafx.h" +#include "SampleGenerator.h" namespace Falcor { - Material::SharedPtr ModelImporter::checkForExistingMaterial(const Material::SharedPtr& pMaterial) + SampleGenerator::SharedPtr SampleGenerator::create(uint32_t type) { - // Check if the material already exists - for(const auto& pMat : mLoadedMaterials) + switch (type) { - if(*pMaterial == *pMat) - { - return pMat; - } + case SAMPLE_GENERATOR_TINY_UNIFORM: + case SAMPLE_GENERATOR_UNIFORM: + return SharedPtr(new SampleGenerator(type)); + default: + logError("Can't create SampleGenerator. Unknown type"); } + return nullptr; + } - // New material - mLoadedMaterials.push_back(pMaterial); - return pMaterial; + void SampleGenerator::prepareProgram(ProgramBase* pProgram) const + { + assert(pProgram); + pProgram->addDefine("SAMPLE_GENERATOR_TYPE", std::to_string(mType)); } } diff --git a/Framework/Source/VR/VrFbo.h b/Source/Falcor/Utils/Sampling/SampleGenerator.h similarity index 52% rename from Framework/Source/VR/VrFbo.h rename to Source/Falcor/Utils/Sampling/SampleGenerator.h index 4cde72219..44eadc815 100644 --- a/Framework/Source/VR/VrFbo.h +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,43 +25,45 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - #pragma once -#include -#include "API/FBO.h" -#include "API/Texture.h" -#include "glm/vec2.hpp" -#include -#include "OpenVR/VRDisplay.h" +#include "Core/Program/Program.h" +#include "SampleGeneratorType.h" namespace Falcor { - class VrFbo + /** Utility class for sample generators on the GPU. + + This class has functions for configuring the shader program and + uploading the necessary lookup tables (if needed). + On the GPU, import SampleGenerator.slang in your shader program. + */ + class dlldecl SampleGenerator : public std::enable_shared_from_this { public: - using UniquePtr = std::unique_ptr; - /** Create a new VrFbo. It will create array resources for color and depth. It will also create views into each array-slice - \param[in] desc FBO description - \param[in] width The width of the FBO. Optional, by default will use the HMD render-target size - \param[in] height The height of the FBO. Optional, by default will use the HMD render-target size - */ - static UniquePtr create(const Fbo::Desc& desc, uint32_t width = 0, uint32_t height = 0); + using SharedPtr = std::shared_ptr; + using SharedConstPtr = std::shared_ptr; - /** Submit the color target into the HMD + virtual ~SampleGenerator() = default; + + /** Factory function for creating a sample generator of the specified type. See SampleGeneratorType.h. */ - void submitToHmd(RenderContext* pRenderCtx) const; + static SharedPtr create(uint32_t type); - /** Get the FBO + /** Prepares a program for use of this sample generator. + Note that the ProgramVars object has to be created _after_ this call. + \param[in] pProgram Program to configure. */ - Fbo::SharedPtr getFbo() const { return mpFbo; } + virtual void prepareProgram(ProgramBase* pProgram) const; - /** Get the resource view to an eye's resource view + /** Binds the data to a program vars object. + \param[in] pVars ProgramVars of the program to set data into. + \return false if there was an error, true otherwise. */ - Texture::SharedPtr getEyeResourceView(VRDisplay::Eye eye) const { return (eye == VRDisplay::Eye::Left) ? mpLeftView : mpRightView; } + virtual bool setIntoProgramVars(ProgramVars* pVars) const { return true; } + + protected: + SampleGenerator(uint32_t type) : mType(type) {} - private: - Fbo::SharedPtr mpFbo; - Texture::SharedPtr mpLeftView; - Texture::SharedPtr mpRightView; + const uint32_t mType; ///< Type of sample generator. See SampleGeneratorType.h. }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.slang b/Source/Falcor/Utils/Sampling/SampleGenerator.slang new file mode 100644 index 000000000..5c4b2d2e5 --- /dev/null +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.slang @@ -0,0 +1,103 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +#include "SampleGeneratorType.h" + +/** The host sets the SAMPLE_GENERATOR_TYPE define to select sample generator. + + This code typedefs the chosen type to the type 'SampleGenerator'. + All sample generators adheres to the same interface, but note that the + size of the 'SampleGenerator' type may vary depending on their state size. + + If SAMPLE_GENERATOR_TYPE is not defined, a compile-time error is printed. + + The 'SampleGeneratorPadded' type holds a SampleGenerator plus additional + padding to make the struct a multiple of 16B. +*/ + +#if defined(SAMPLE_GENERATOR_TYPE) && SAMPLE_GENERATOR_TYPE == SAMPLE_GENERATOR_TINY_UNIFORM + import Utils.Sampling.TinyUniformSampleGenerator; + typedef TinyUniformSampleGenerator SampleGenerator; + + struct SampleGeneratorPadded + { + SampleGenerator internal; + uint3 _pad; + }; + +#elif defined(SAMPLE_GENERATOR_TYPE) && SAMPLE_GENERATOR_TYPE == SAMPLE_GENERATOR_UNIFORM + import Utils.Sampling.UniformSampleGenerator; + typedef UniformSampleGenerator SampleGenerator; + + struct SampleGeneratorPadded + { + SampleGenerator internal; + }; + +#else + // Compile-time error if the SAMPLE_GENERATOR_TYPE define is not set + // or does not have a valid value. Picking a default generator to use + // in this case would lead to hard-to-debug errors. + #error SAMPLE_GENERATOR_TYPE is not set to a supported type. See SampleGeneratorType.h. +#endif + + +/** Convenience functions for generating 1D/2D/3D values in the range [0,1). + + Note: These are global instead of member functions in the sample generator + interface, as there seems to be no way in Slang currently to specify default + implementations without duplicating the code into all classes that implement + the interace. +*/ + +float sampleNext1D(inout SampleGenerator sg) +{ + // Use upper 24 bits and divide by 2^24 to get a number u in [0,1). + // In floating-point precision this also ensures that 1.0-u != 0.0. + uint bits = sg.next(); + return (bits >> 8) * 0x1p-24; +} + +float2 sampleNext2D(inout SampleGenerator sg) +{ + float2 sample; + // Don't use the float2 initializer to ensure consistent order of evaluation. + sample.x = sampleNext1D(sg); + sample.y = sampleNext1D(sg); + return sample; +} + +float3 sampleNext3D(inout SampleGenerator sg) +{ + float3 sample; + // Don't use the float3 initializer to ensure consistent order of evaluation. + sample.x = sampleNext1D(sg); + sample.y = sampleNext1D(sg); + sample.z = sampleNext1D(sg); + return sample; +} diff --git a/Samples/Effects/HashedAlpha/Data/HashedAlpha.ps.hlsl b/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang similarity index 85% rename from Samples/Effects/HashedAlpha/Data/HashedAlpha.ps.hlsl rename to Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang index 17541b2e0..4ac915bdb 100644 --- a/Samples/Effects/HashedAlpha/Data/HashedAlpha.ps.hlsl +++ b/Source/Falcor/Utils/Sampling/SampleGeneratorInterface.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import ShaderCommon; -__import Shading; -__import DefaultVS; -float4 main(VertexOut vOut) : SV_TARGET0 +/** Slang interface for sample generator implementations. +*/ +interface ISampleGenerator { - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); - return float4(sd.diffuse, 1); -} + /** Returns the next sample value. This function updates the state. + */ + [mutating] uint next(); +}; diff --git a/Source/Falcor/Utils/Sampling/SampleGeneratorType.h b/Source/Falcor/Utils/Sampling/SampleGeneratorType.h new file mode 100644 index 000000000..ba7ad65e2 --- /dev/null +++ b/Source/Falcor/Utils/Sampling/SampleGeneratorType.h @@ -0,0 +1,31 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +#define SAMPLE_GENERATOR_TINY_UNIFORM 0 +#define SAMPLE_GENERATOR_UNIFORM 1 diff --git a/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang b/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang new file mode 100644 index 000000000..747c2980a --- /dev/null +++ b/Source/Falcor/Utils/Sampling/TinyUniformSampleGenerator.slang @@ -0,0 +1,62 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Utils.Math.HashUtils; +import Utils.Math.BitTricks; +import Utils.Sampling.Pseudorandom.LCG; +import Utils.Sampling.SampleGeneratorInterface; + +/** Tiny uniform random sample generator. + + This generator has only 32 bit state and sub-optimal statistical properties. + Do not use for anything critical; correlation artifacts may be prevalent. + + This code works on shader model 5.1 and above. +*/ +struct TinyUniformSampleGenerator : ISampleGenerator +{ + /** Create sample generator. + */ + static TinyUniformSampleGenerator create(uint2 pixel, uint sampleNumber) + { + TinyUniformSampleGenerator sampleGenerator; + + // Use block cipher to generate a pseudorandom initial seed. + uint seed = blockCipherTEA(interleave_32bit(pixel), sampleNumber).x; + sampleGenerator.rng = createLCG(seed); + return sampleGenerator; + } + + /** Returns the next sample value. This function updates the state. + */ + [mutating] uint next() + { + return nextRandom(rng); + } + + LCG rng; ///< Simple LCG 32-bit pseudorandom number generator. +}; diff --git a/Source/Falcor/Utils/Sampling/UniformSampleGenerator.slang b/Source/Falcor/Utils/Sampling/UniformSampleGenerator.slang new file mode 100644 index 000000000..cd049c402 --- /dev/null +++ b/Source/Falcor/Utils/Sampling/UniformSampleGenerator.slang @@ -0,0 +1,73 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Utils.Math.BitTricks; +import Utils.Sampling.Pseudorandom.Xoshiro; +import Utils.Sampling.Pseudorandom.SplitMix64; +import Utils.Sampling.SampleGeneratorInterface; + +/** Default uniform pseudorandom number generator. + + This generator has 128 bit state and should have acceptable statistical + properties for most rendering applications. + + This sample generator requires shader model 6.0 or above. +*/ +struct UniformSampleGenerator : ISampleGenerator +{ + /** Create sample generator. + */ + static UniformSampleGenerator create(uint2 pixel, uint sampleNumber) + { + UniformSampleGenerator sampleGenerator; + + // Use SplitMix64 generator to generate a good pseudorandom initial state. + // The pixel coord is expected to be max 28 bits (16K^2 is the resource limit in D3D12). + // The sample number is expected to be practically max ~28 bits, e.g. 16spp x 16M samples. + // As long as both stay <= 32 bits, we will always have a unique initial seed. + // This is however no guarantee that the generated sequences will never overlap, + // but it is very unlikely. For example, with these most extreme parameters of + // 2^56 sequences of length L, the probability of overlap is P(overlap) = L*2^-16. + SplitMix64 rng = createSplitMix64(interleave_32bit(pixel), sampleNumber); + uint64_t s0 = nextRandom64(rng); + uint64_t s1 = nextRandom64(rng); + uint seed[4] = { uint(s0), uint(s0 >> 32), uint(s1), uint(s1 >> 32) }; + + // Create xoshiro128** pseudorandom generator. + sampleGenerator.rng = createXoshiro128StarStar(seed); + return sampleGenerator; + } + + /** Returns the next sample value. This function updates the state. + */ + [mutating] uint next() + { + return nextRandom(rng); + } + + Xoshiro128StarStar rng; +}; diff --git a/Source/Falcor/Utils/Scripting/Console.cpp b/Source/Falcor/Utils/Scripting/Console.cpp new file mode 100644 index 000000000..5ac487168 --- /dev/null +++ b/Source/Falcor/Utils/Scripting/Console.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Console.h" +#include "dear_imgui/imgui.h" + +namespace Falcor +{ + namespace + { + static const uint32_t kLineCount = 16; + class GuiWindow + { + public: + GuiWindow(Gui* pGui) : mpGui(pGui) + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + height = (float)ImGui::GetTextLineHeight() * kLineCount; + ImGui::SetNextWindowSize({ ImGui::GetIO().DisplaySize.x, 0 }, ImGuiCond_Always); + ImGui::SetNextWindowPos({0, ImGui::GetIO().DisplaySize.y - height}, ImGuiCond_Always); + + ImGui::Begin("##Console", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushFont(pGui->getFont("monospace")); + } + + ~GuiWindow() + { + ImGui::PopFont(); + ImGui::PopStyleVar(); + ImGui::End(); + ImGui::PopStyleVar(); + } + + float height = 0; + private: + Gui* mpGui; + }; + + std::string sLog; + char sCmd[2048] = {}; + bool sFlush = false; + bool scrollToBottom = true; + + SCRIPT_BINDING(Console) + { + auto cls = []() {sLog = {}; }; + m.func_("cls", cls); + } + } + + bool Console::flush() + { + if (!sFlush) return false; + std::string cmd(sCmd); // We need to use a temporary copy so that we could reset `sCmd`, otherwise we will end up with an endless loop + sCmd[0] = 0; + sFlush = false; + + try + { + sLog += Scripting::runScript(cmd); + } + catch (std::exception e) + { + sLog += std::string(e.what()) + "\n"; + }; + return true; + } + + void Console::render(Gui* pGui) + { + GuiWindow w(pGui); + + ImGui::BeginChild("log", {0, w.height - ImGui::GetTextLineHeight() - 5 }); + ImGui::TextUnformatted(sLog.c_str()); + if(scrollToBottom) + { + ImGui::SetScrollHere(1.0f); + scrollToBottom = false; + } + ImGui::EndChild(); + + ImGui::PushItemWidth(ImGui::GetWindowWidth()); + if(ImGui::InputText("##console", sCmd, arraysize(sCmd), ImGuiInputTextFlags_EnterReturnsTrue)) + { + sFlush = true; + sLog += std::string(sCmd) + "\n"; + scrollToBottom = true; + ImGui::SetKeyboardFocusHere(); + ImGui::GetIO().KeysDown[(uint32_t)KeyboardEvent::Key::Enter] = false; + } + if(ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere(); + pGui->setActiveFont(""); + } +} diff --git a/Framework/Source/API/Vulkan/VKProgramVersion.cpp b/Source/Falcor/Utils/Scripting/Console.h similarity index 86% rename from Framework/Source/API/Vulkan/VKProgramVersion.cpp rename to Source/Falcor/Utils/Scripting/Console.h index 0d31b550a..36774aa31 100644 --- a/Framework/Source/API/Vulkan/VKProgramVersion.cpp +++ b/Source/Falcor/Utils/Scripting/Console.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Graphics/Program/ProgramVersion.h" +#pragma once +#include "ScriptBindings.h" +#include namespace Falcor { - void ProgramVersion::deleteApiHandle() + class dlldecl Console { - - } - - bool ProgramVersion::init(std::string& log) - { - return true; - } + public: + static void render(Gui* pGui); + static bool flush(); + }; } diff --git a/Framework/Source/Utils/Dictionary.h b/Source/Falcor/Utils/Scripting/Dictionary.h similarity index 98% rename from Framework/Source/Utils/Dictionary.h rename to Source/Falcor/Utils/Scripting/Dictionary.h index f07990889..ddabd342f 100644 --- a/Framework/Source/Utils/Dictionary.h +++ b/Source/Falcor/Utils/Scripting/Dictionary.h @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Externals/pybind11/include/pybind11/pybind11.h" namespace Falcor { @@ -103,4 +102,4 @@ namespace Falcor private: Container mMap; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp new file mode 100644 index 000000000..ceaf5b4d3 --- /dev/null +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "ScriptBindings.h" +#include "pybind11/embed.h" + +namespace Falcor::ScriptBindings +{ + ClassesMap sClasses; + std::unordered_map sEnumNames; + + namespace + { + /** `gBindFuncs` is declared as pointer so that we can ensure it can be explicitly + allocated when registerBinding() is called. (The C++ static objectinitialization fiasco.) + */ + std::vector* gBindFuncs = nullptr; + } + + void registerBinding(BindComponentFunc f) + { + if(Scripting::isRunning()) + { + try + { + auto pymod = pybind11::module::import("falcor"); + Module m(pymod); + f(m); + // Re-import falcor + pybind11::exec("from falcor import *"); + } + catch (const std::exception &e) + { + PyErr_SetString(PyExc_ImportError, e.what()); + logError(e.what()); + return; + } + } + else + { + if (!gBindFuncs) gBindFuncs = new std::vector(); + gBindFuncs->push_back(f); + } + } + + PYBIND11_EMBEDDED_MODULE(falcor, m) + { + // Alias python's True/False to true/false + m.attr("true") = true; + m.attr("false") = false; + + if (gBindFuncs) + { + ScriptBindings::Module fm(m); + for (auto f : *gBindFuncs) f(fm); + } + } +} diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.h b/Source/Falcor/Utils/Scripting/ScriptBindings.h new file mode 100644 index 000000000..dc22a9a63 --- /dev/null +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.h @@ -0,0 +1,250 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "pybind11/stl.h" + +namespace Falcor::ScriptBindings +{ + struct enable_to_string {}; + class Module; + + // Helper to check if a class has a `SharedPtr`. If it is, we're using it as the internal python object + template + struct has_shared_ptr : std::false_type {}; + + template + struct has_shared_ptr> : std::true_type {}; + + /************************************************************************/ + /* Namespace definitions */ + /************************************************************************/ + struct ClassDesc + { + ClassDesc() = default; + ClassDesc(const std::string& n) : name(n) {}; + struct Funcs + { + std::function setF; + std::function printF; + }; + std::unordered_map funcs; + std::string name; + }; + + using ClassesMap = std::unordered_map; + dlldecl extern ClassesMap sClasses; + dlldecl extern std::unordered_map sEnumNames; + + using BindComponentFunc = std::function; + dlldecl void registerBinding(BindComponentFunc f); + + /** A custom to_string() that handles registered classes + */ + using Falcor::to_string; + + template + typename std::enable_if_t, std::string> to_string(const T& t) + { + assert(sClasses.find(typeid(T)) != sClasses.end()); + std::string s = sClasses.at(typeid(T)).name + '('; + bool first = true; + for (const auto a : sClasses.at(typeid(T)).funcs) + { + if (!first) s += ", "; + first = false; + s += a.second.printF(&t); + } + return s + ")"; + } + + /************************************************************************/ + /* Class */ + /************************************************************************/ + template + class Class + { + public: + template + Class& rwField(const char* name, D std::remove_pointer_t::* pm, const Extra&... extra) + { + auto setF = [pm](void* pObj, pybind11::handle h) { static_cast(pObj)->*pm = h.cast(); }; + std::string nameStr(name); + auto printF = [pm, nameStr](const void* pObj) + { + auto s = nameStr + "="; + if constexpr(std::is_enum_v) s += sEnumNames.at(typeid(D)) + "."; + s += to_string(static_cast(pObj)->*pm); + return s; + }; + + sClasses[typeid(T)].funcs[name] = { setF, printF }; + pyclass.def_readwrite(name, pm, extra...); + return *this; + } + + template + Class& func_(const char* name, Func&& f, const Extra&... extra) + { + pyclass.def(name, std::forward(f), extra...); + return *this; + } + + template + Class& ctor(Func&& f, const Extra&... extra) + { + pyclass.def(pybind11::init(f), extra...); + return *this; + } + + template + Class& staticFunc_(const char* name, Func&& f, const Extra&... extra) + { + pyclass.def_static(name, std::forward(f), extra...); + return *this; + } + private: + friend Module; + + Class(const char* name, pybind11::module& m) : pyclass(m, name) + { + if constexpr(std::is_default_constructible_v && std::is_copy_constructible_v) + { + sClasses[typeid(T)] = ClassDesc(name); + auto initFunc = [](const pybind11::kwargs& args) + { + T t; + const auto& classBindings = sClasses.at(typeid(T)).funcs; + for (auto a : args) classBindings.at(a.first.cast()).setF(&t, a.second); + return t; + }; + pyclass.def(pybind11::init(initFunc)).def(pybind11::init<>()); + if constexpr(std::is_base_of_v) pyclass.def("__repr__", to_string); + } + } + pybind11::class_ pyclass; + }; + + /************************************************************************/ + /* Enum */ + /************************************************************************/ + template + class Enum + { + public: + Enum& value(const char* name, T value) + { + pyenum.value(name, value); + return *this; + } + private: + friend Module; + Enum(const char* name, pybind11::module& m) : pyenum(m, name) { sEnumNames[typeid(T)] = name; } + pybind11::enum_ pyenum; + }; + + /************************************************************************/ + /* Module */ + /************************************************************************/ + class Module + { + public: + // An overload of class_ which will be invoked if the object has SharedPtr + template + auto class_(const char* name) + { + if (classExists()) + { + throw std::runtime_error((std::string("Class ") + name + " was already registered").c_str()); + } + + if constexpr(has_shared_ptr::value) + { + return Class(name, mModule); + } + else + { + return Class(name, mModule); + } + } + + template + Enum enum_(const char* name) + { + return Enum(name, mModule); + } + + template + Module& func_(const char* name, Func&& f, const Extra&... extra) + { + mModule.def(name, std::forward(f), extra...); + return *this; + } + + Module(pybind11::module& m) : mModule(m) {} + + template + bool classExists() const + { + try + { + pybind11::dict d; + d["test"] = (T*)nullptr; + return true; + } + catch (std::exception) { return false; } + } + private: + pybind11::module& mModule; + }; + + using pybind11::overload_cast; + using pybind11::const_; + + /************************************************************************/ + /* Helpers */ + /************************************************************************/ + +#ifndef _staticlibrary +#define SCRIPT_BINDING(Name) \ + static void ScriptBinding##Name(ScriptBindings::Module& m); \ + struct ScriptBindingRegisterer##Name { \ + ScriptBindingRegisterer##Name() \ + { \ + ScriptBindings::registerBinding(ScriptBinding##Name); \ + } \ + } gScriptBinding##Name; \ + static void ScriptBinding##Name(ScriptBindings::Module& m) /* over to the user for the braces */ +#else +#define SCRIPT_BINDING(Name) static_assert(false, "Using SCRIPT_BINDING() in a static-library is not supported. The C++ linker usually doesn't pull static-initializers into the EXE. " \ + "Call `registerBinding()` yourself from a code that is guarenteed to run."); + +#endif // _library + +#define regEnumVal(a) value(to_string(a).c_str(), a) +#define regClass(c_) class_(#c_); +} diff --git a/Framework/Source/Utils/Scripting/Scripting.cpp b/Source/Falcor/Utils/Scripting/Scripting.cpp similarity index 72% rename from Framework/Source/Utils/Scripting/Scripting.cpp rename to Source/Falcor/Utils/Scripting/Scripting.cpp index 4a19de8a8..8ec09fef1 100644 --- a/Framework/Source/Utils/Scripting/Scripting.cpp +++ b/Source/Falcor/Utils/Scripting/Scripting.cpp @@ -25,21 +25,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Scripting.h" +#include #include "pybind11/embed.h" -#include "pybind11/stl.h" -#include "Utils/StringUtils.h" -#include "Utils/Dictionary.h" -// TEST -#include "API/Formats.h" -#include "Experimental/RenderGraph/RenderGraphScripting.h" -#include "ScriptBindings.h" - -using namespace pybind11::literals; namespace Falcor { + const FileDialogFilterVec Scripting::kFileExtensionFilters = { { "py", "Script Files"} }; + bool Scripting::sRunning = false; + template static bool insertNewValue(const std::pair& pyVar, Dictionary& falcorDict) { @@ -84,14 +79,6 @@ namespace Falcor return true; } - PYBIND11_EMBEDDED_MODULE(falcor, m) - { - ScriptBindings::registerScriptingObjects(m); - RenderGraphScripting::registerScriptingObjects(m); - } - - bool Scripting::sRunning = false; - bool Scripting::start() { if (!sRunning) @@ -123,39 +110,65 @@ namespace Falcor { sRunning = false; pybind11::finalize_interpreter(); + ScriptBindings::sClasses.clear(); } } - static bool runScript(const std::string& script, std::string& errorLog, pybind11::dict& locals) + class RedirectStdout { - try + public: + RedirectStdout() + { + auto m = pybind11::module::import("sys"); + mOrigOut = m.attr("stdout"); + mBuffer = pybind11::module::import("io").attr("StringIO")(); + m.attr("stdout") = mBuffer; + } + + ~RedirectStdout() { - pybind11::exec(script.c_str(), pybind11::globals(), locals); + pybind11::module::import("sys").attr("stdout") = mOrigOut; } - catch (const std::runtime_error& e) + + operator std::string() const { - errorLog = e.what(); - return false; + mBuffer.attr("seek")(0); + return pybind11::str(mBuffer.attr("read")()); } - return true; + private: + pybind11::object mOrigOut; + pybind11::object mBuffer; + }; + + static std::string runScript(const std::string& script, pybind11::dict& locals) + { + RedirectStdout rs; + pybind11::exec(script.c_str(), pybind11::globals(), locals); + return rs; } - bool Scripting::runScript(const std::string& script, std::string& errorLog) + std::string Scripting::runScript(const std::string& script) { auto ref = pybind11::globals(); - return Falcor::runScript(script, errorLog, ref); + return Falcor::runScript(script, ref); } - bool Scripting::runScript(const std::string& script, std::string& errorLog, Context& context) + std::string Scripting::runScript(const std::string& script, Context& context) { - return Falcor::runScript(script, errorLog, context.mLocals); + return Falcor::runScript(script, context.mLocals); } - Scripting::Context Scripting::getGlobalContext() const + Scripting::Context Scripting::getGlobalContext() { Context c; c.mLocals = pybind11::globals(); return c; } + + std::string Scripting::runScriptFromFile(const std::string& filename, Context& context) + { + if (std::filesystem::exists(filename)) return Scripting::runScript(readFile(filename), context); + throw std::exception(std::string("Scripting::runScriptFromFile() - Can't find the file `" + filename + "`").c_str()); + } } diff --git a/Framework/Source/Utils/Scripting/Scripting.h b/Source/Falcor/Utils/Scripting/Scripting.h similarity index 59% rename from Framework/Source/Utils/Scripting/Scripting.h rename to Source/Falcor/Utils/Scripting/Scripting.h index b2095d3c2..20d5b4c72 100644 --- a/Framework/Source/Utils/Scripting/Scripting.h +++ b/Source/Falcor/Utils/Scripting/Scripting.h @@ -26,13 +26,21 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "pybind11/pybind11.h" +#include "ScriptBindings.h" +#include +#include "Utils/StringUtils.h" + +using namespace pybind11::literals; namespace Falcor { - class Scripting + class Gui; + + class dlldecl Scripting { public: + static const FileDialogFilterVec kFileExtensionFilters; + class Context { public: @@ -40,6 +48,7 @@ namespace Falcor struct ObjectDesc { ObjectDesc(const std::string& name_, const T& obj_) : name(name_), obj(obj_) {} + operator const T&() const { return obj; } std::string name; T obj; }; @@ -52,7 +61,10 @@ namespace Falcor { try { - v.push_back(ObjectDesc(l.first.cast(), l.second.cast())); + if(!l.second.is_none()) + { + v.push_back(ObjectDesc(l.first.cast(), l.second.cast())); + } } catch (std::exception&) {} } @@ -77,10 +89,47 @@ namespace Falcor static bool start(); static void shutdown(); - static bool runScript(const std::string& script, std::string& errorLog); - static bool runScript(const std::string& script, std::string& errorLog, Context& context); - Context getGlobalContext() const; + static std::string runScript(const std::string& script); + static std::string runScript(const std::string& script, Context& context); + static std::string runScriptFromFile(const std::string& filename, Context& context); + static Context getGlobalContext(); + static bool isRunning() { return sRunning; } + + static std::string makeFunc(const std::string& func) + { + return func + "()\n"; + } + + template + static std::string getArgString(const T& arg) + { + std::string a; + if (std::is_enum_v) a += getEnumTypeName(arg) + "."; + return a + to_string(arg); + } + + template + static std::string makeFunc(const std::string& func, Arg first, Args...args) + { + std::string s = func + "(" + getArgString(first); + int32_t dummy[] = { 0, (s += ", " + getArgString(args), 0)... }; + s += ")\n"; + return s; + } + + static std::string makeMemberFunc(const std::string& var, const std::string& func) + { + return std::string(var) + "." + makeFunc(func); + } + + template + static std::string makeMemberFunc(const std::string& var, const std::string& func, Arg first, Args...args) + { + std::string s(var); + s += std::string(".") + makeFunc(func, first, args...); + return s; + } private: static bool sRunning; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/StringUtils.h b/Source/Falcor/Utils/StringUtils.h similarity index 88% rename from Framework/Source/Utils/StringUtils.h rename to Source/Falcor/Utils/StringUtils.h index 567408371..fb81478fa 100644 --- a/Framework/Source/Utils/StringUtils.h +++ b/Source/Falcor/Utils/StringUtils.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,6 +33,20 @@ namespace Falcor { + // String/string_View append operators missing from the spec + inline std::string operator+(const std::string& lhs, std::string_view rhs) + { + std::string s = lhs; + s.append(rhs); + return s; + } + + inline std::string& operator+=(std::string& lhs, std::string_view rhs) + { + lhs.append(rhs); + return lhs; + } + /*! * \addtogroup Falcor * @{ @@ -277,5 +291,32 @@ namespace Falcor return s; } + /** Return string name of class type from a pointer to an object + */ + template + std::string getClassTypeName(const T* ptr = nullptr) + { + std::string typeName = typeid(*ptr).name(); +#ifdef _WIN32 + assert(hasPrefix(typeName, "class ")); + auto v = splitString(typeName.substr(6), "::"); + return v.back(); +#else +#error getClassTypeName() not implemented for this platform +#endif + } + + template + std::string getEnumTypeName(const T& e = T(0)) + { + std::string typeName = typeid(e).name(); +#ifdef _WIN32 + assert(hasPrefix(typeName, "enum ")); + auto v = splitString(typeName.substr(6), "::"); + return v.back(); +#else +#error getClassTypeName() not implemented for this platform +#endif + } /*! @} */ }; diff --git a/Source/Falcor/Utils/Threading.cpp b/Source/Falcor/Utils/Threading.cpp new file mode 100644 index 000000000..ffb2edeb0 --- /dev/null +++ b/Source/Falcor/Utils/Threading.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Threading.h" + +namespace Falcor +{ + namespace + { + struct ThreadingData + { + bool initialized = false; + std::vector threads; + uint32_t current; + } gData; + } + + void Threading::start(uint32_t threadCount) + { + if (gData.initialized) return; + + gData.threads.resize(threadCount); + gData.initialized = true; + } + + void Threading::shutdown() + { + for (auto& t : gData.threads) + { + if (t.joinable()) t.join(); + } + + gData.initialized = false; + } + + Threading::Task Threading::dispatchTask(const std::function& func) + { + assert(gData.initialized); + + std::thread& t = gData.threads[gData.current]; + if (t.joinable()) t.join(); + t = std::thread(func); + gData.current = (gData.current + 1) % gData.threads.size(); + + return Task(); + } + + Threading::Task::Task() + { + } + + bool Threading::Task::isRunning() + { + logError("Threading::Task::isRunning() not implemented"); + return true; + } + + void Threading::Task::finish() + { + logError("Threading::Task::finish() not implemented"); + } +} diff --git a/Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.h b/Source/Falcor/Utils/Threading.h similarity index 58% rename from Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.h rename to Source/Falcor/Utils/Threading.h index 70dc549c2..7279fc4a1 100644 --- a/Framework/Source/Graphics/Model/Loaders/BinaryModelImporter.h +++ b/Source/Falcor/Utils/Threading.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,39 +26,51 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include "Utils/BinaryFileStream.h" -#include "glm/vec3.hpp" -#include "../Model.h" -#include "Graphics/Model/Loaders/ModelImporter.h" +#include namespace Falcor { - class Texture; - - class BinaryModelImporter : public ModelImporter + class dlldecl Threading { public: - /** import a new model from internal binary format - \param[in] filename Model's filename. Loader will look for it in the data directories. - \param[in] flags Flags controlling model creation - returns nullptr if loading failed, otherwise a new Model object - */ - static bool import(Model& model, const std::string& filename, Model::LoadFlags flags); + const static uint32_t kDefaultThreadCount = 16; - private: - BinaryModelImporter(const std::string& fullpath); - bool importModel(Model& model, Model::LoadFlags flags); + /** Handle to a dispatched task - std::string mModelName; - BinaryFileStream mStream; - - struct TangentSpace + TODO: Implementation + */ + class Task { - glm::vec3 tangent; - glm::vec3 bitangent; + public: + /** Check if task is still executing + */ + bool isRunning(); + + /** Wait for task to finish executing + */ + void finish(); + + private: + Task(); + friend class Threading; }; - static const uint32_t kInvalidOffset = uint32_t(-1); + /** Initializes the global thread pool + \param[in] threadCount Number of threads in the pool + */ + static void start(uint32_t threadCount = kDefaultThreadCount); + + /** Waits for all currently executing threads to finish and shuts down the thread pool + */ + static void shutdown(); + + /** Returns the maximum number of concurrent threads supported by the hardware + */ + static uint32_t getLogicalThreadCount() { return std::thread::hardware_concurrency(); } + + /** Starts a task on an available thread. + \return Handle to the task + */ + static Task dispatchTask(const std::function& func); }; } diff --git a/Source/Falcor/Utils/Timing/Clock.cpp b/Source/Falcor/Utils/Timing/Clock.cpp new file mode 100644 index 000000000..7518da166 --- /dev/null +++ b/Source/Falcor/Utils/Timing/Clock.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Clock.h" + +namespace Falcor +{ + namespace + { + constexpr char kNow[] = "now"; + constexpr char kPause[] = "pause"; + constexpr char kPlay[] = "play"; + constexpr char kStop[] = "stop"; + constexpr char kSimFps[] = "fpsSim"; + constexpr char kFrame[] = "frame"; + constexpr char kStep[] = "step"; + constexpr char kFramerate[] = "framerate"; + + std::optional fpsDropdown(Gui::Window& w, uint32_t curVal) + { + static const uint32_t commonFps[] = { 0, 24, 25, 30, 48, 50, 60, 75, 90, 120, 144, 200, 240, 360, 480 }; + static const uint32_t kCustom = uint32_t(-1); + + static auto dropdown = []() + { + Gui::DropdownList d; + for (auto f : commonFps) d.push_back({ f, f == 0 ? "Disabled" : to_string(f) }); + d.push_back({ kCustom, "Custom" }); + return d; + }(); + + uint32_t index = [curVal]() + { + for (auto f : commonFps) if (f == curVal) return f; + return kCustom; + }(); + + bool changed = w.dropdown("FPS", dropdown, index); + if (index == kCustom) + { + changed = w.var("Custom##fps", curVal, 0u, UINT32_MAX, 1u, nullptr, false); + } + else curVal = index; + + return changed ? std::optional(curVal) : std::nullopt; + } + + struct ClockTextures + { + Texture::SharedPtr pPlay; + Texture::SharedPtr pPause; + Texture::SharedPtr pRewind; + Texture::SharedPtr pStop; + Texture::SharedPtr pNextFrame; + Texture::SharedPtr pPrevFrame; + } gClockTextures; + + constexpr uint64_t kTicksPerSecond = 14400 * (1 << 16); // 14400 is a common multiple of our supported frame-rates. 2^16 gives 64K intra-frame steps + + double timeFromFrame(uint64_t frame, uint64_t ticksPerFrame) + { + return double(frame * ticksPerFrame) / (double)kTicksPerSecond; + } + + uint64_t frameFromTime(double seconds, uint64_t ticksPerFrame) + { + return uint64_t(seconds * (double)kTicksPerSecond) / ticksPerFrame; + } + } + + Clock::Clock() { now(0); } + + Clock& Clock::framerate(uint32_t fps) + { + mFramerate = fps; + mTicksPerFrame = 0; + if(fps) + { + if (kTicksPerSecond % fps) logWarning("Clock::framerate() - requesetd FPS can't be accurately representated. Expect roudning errors"); + mTicksPerFrame = kTicksPerSecond / fps; + } + + if(!mDeferredFrameID && !mDeferredTime) now(mTime.now); + return *this; + } + + Clock& Clock::tick() + { + if (mDeferredFrameID) frame(mDeferredFrameID.value()); + else if (mDeferredTime) now(mDeferredTime.value()); + else if(!mPaused) step(); + return *this; + } + + void Clock::updateTimer() + { + mTimer.update(); + mRealtime.update(mRealtime.now + mTimer.delta()); + } + + void Clock::resetDeferredObjects() + { + mDeferredTime = std::nullopt; + mDeferredFrameID = std::nullopt; + } + + Clock& Clock::now(double seconds, bool deferToNextTick) + { + resetDeferredObjects(); + + if (deferToNextTick) + { + mDeferredTime = seconds; + } + else + { + updateTimer(); + if (mFramerate) + { + mFrames = frameFromTime(seconds, mTicksPerFrame); + seconds = timeFromFrame(mFrames, mTicksPerFrame); + } + else mFrames = 0; + + mTime.delta = mTime.now - seconds; + mTime.now = seconds; + } + return *this; + } + + Clock& Clock::frame(uint64_t f, bool deferToNextTick) + { + resetDeferredObjects(); + + if (deferToNextTick) + { + mDeferredFrameID = f; + } + else + { + updateTimer(); + mFrames = f; + if (mFramerate) + { + double secs = timeFromFrame(mFrames, mTicksPerFrame); + mTime.delta = mTime.now - secs; + mTime.now = secs; + } + } + return *this; + } + + Clock& Clock::play() + { + updateTimer(); + mPaused = false; + return *this; + } + + Clock& Clock::step(int64_t frames) + { + if (frames < 0 && uint64_t(-frames) > mFrames) mFrames = 0; + else mFrames += frames; + + updateTimer(); + double t = simulatingFps() ? timeFromFrame(mFrames, mTicksPerFrame) : ((mTimer.delta() * mScale) + mTime.now); + mTime.update(t); + return *this; + } + + void Clock::renderUI(Gui::Window& w) + { + const auto& tex = gClockTextures; + + float time = (float)now(); + float scale = (float)timeScale(); + if (w.var("Time##Cur", time, 0.f, FLT_MAX, 0.001f, false, "%.3f")) now(time); + if (!simulatingFps() && w.var("Scale", scale)) timeScale(scale); + bool showStep = mPaused && simulatingFps(); + + float indent = showStep ? 10.0f : 60.0f; + w.indent(indent); + static const uvec2 iconSize = { 25, 25 }; + if (w.imageButton("Rewind", tex.pRewind, iconSize)) now(0); + if (showStep && w.imageButton("PrevFrame", tex.pPrevFrame, iconSize, true, true)) step(-1); + if (w.imageButton("Stop", tex.pStop, iconSize, true, true)) stop(); + auto pTex = mPaused ? tex.pPlay : tex.pPause; + if (w.imageButton("PlayPause", pTex, iconSize, true, true)) mPaused ? play() : pause(); + if (showStep && w.imageButton("NextFrame", tex.pNextFrame, iconSize, true, true)) step(); + + w.indent(-indent); + + w.separator(2); + w.text("Framerate Simulation"); + w.tooltip("Simulate a constant frame rate. The time will advance by 1/FPS each frame, regardless of the actual frame rendering time"); + + auto fps = fpsDropdown(w, mFramerate); + if (fps) framerate(fps.value()); + + if (simulatingFps()) + { + uint64_t curFrame = frame(); + if (w.var("Frame ID", curFrame)) frame(curFrame); + } + } + + SCRIPT_BINDING(Clock) + { + auto c = m.regClass(Clock); + c.func_(kNow, ScriptBindings::overload_cast<>(&Clock::now, ScriptBindings::const_)); + + auto now = [](Clock* pClock, double secs) {pClock->now(secs, true); }; + c.func_(kNow, now, "seconds"_a); + + c.func_(kFrame, ScriptBindings::overload_cast<>(&Clock::frame, ScriptBindings::const_)); + auto frame = [](Clock* pClock, uint64_t f) {pClock->frame(f, true); }; + c.func_(kFrame, frame, "frameID"_a); + + c.func_(kPause, &Clock::pause); + c.func_(kPlay, &Clock::play); + c.func_(kStop, &Clock::stop); + c.func_(kPause, &Clock::pause); + c.func_(kStep, &Clock::step, "frames"_a = 1); + c.func_(kFramerate, ScriptBindings::overload_cast(&Clock::framerate)); + c.func_(kFramerate, ScriptBindings::overload_cast<>(&Clock::framerate, ScriptBindings::const_)); + } + + void Clock::start() + { + auto loadTexture = [](const std::string& tex) {return Texture::createFromFile("Framework/Textures/" + tex, false, true); }; + gClockTextures.pRewind = loadTexture("Rewind.jpg"); + gClockTextures.pPlay = loadTexture("Play.jpg"); + gClockTextures.pPause = loadTexture("Pause.jpg"); + gClockTextures.pStop = loadTexture("Stop.jpg"); + gClockTextures.pNextFrame = loadTexture("NextFrame.jpg"); + gClockTextures.pPrevFrame = loadTexture("PrevFrame.jpg"); + } + + void Clock::shutdown() + { + gClockTextures = {}; + } + + std::string Clock::getScript(const std::string& var) const + { + std::string s; + s += Scripting::makeMemberFunc(var, kNow, 0); + s += Scripting::makeMemberFunc(var, kFramerate, mFramerate); + s += std::string("# If ") + kFramerate + "() is not zero, you can use the following function to set the start frame\n"; + s += "# " + Scripting::makeMemberFunc(var, kFrame, 0); + if (mPaused) s += Scripting::makeMemberFunc(var, kPause); + return s; + } +} diff --git a/Source/Falcor/Utils/Timing/Clock.h b/Source/Falcor/Utils/Timing/Clock.h new file mode 100644 index 000000000..c1c411e7c --- /dev/null +++ b/Source/Falcor/Utils/Timing/Clock.h @@ -0,0 +1,166 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "CpuTimer.h" +#include "Utils/UI/Gui.h" + +namespace Falcor +{ + /** A clock. This class supports both real-time clock (based on the system's clock) and a fixed time-step clock (based on tick count) + */ + class dlldecl Clock + { + public: + Clock(); + + /** Start the system + */ + static void start(); + + /** End the system + */ + static void shutdown(); + + /** Set the current time + \param[in] seconds The time in seconds + \param[in] deferToNextTick Apply the change on the next tick. No changes will be made to the clock until the next tick + */ + Clock& now(double seconds, bool deferToNextTick = false); + + /** Get the time of the last `tick()` call + */ + double now() const { return mTime.now; } + + /** Get the time delta between the 2 previous ticks. This function respects the FPS simulation setting + Note that due to floating-point precision, this function won't necessarily return exactly (1/FPS) when simulating framerate. + This function will potentially return a negative number, for example when resetting the time to zero + */ + double delta() const { return mTime.delta; } + + /** Set the current frame ID. Calling this will cause the next `tick()` call to be skipped + When running in real-time mode, it will only change the frame number without affecting the time + When simulating FPS, it will change the time to match the current frame ID + \param[in] seconds The frame ID + \param[in] deferToNextTick Apply the change on the next tick. No changes will be made to the clock until the next tick + */ + Clock& frame(uint64_t f, bool deferToNextTick = false); + + /** Get the current frame ID. + When running in real-time mode, this is the number of frames since the last time the time was set. + When simulating FPS, the number of frames according to the time + */ + uint64_t frame() const { return mFrames; } + + /** Get the real-time delta between the 2 previous ticks. + This function returns the actual time that passed between the 2 `tick()` calls. It doesn't any time-manipulation setting like time-scaling and FPS simulation + */ + double realTimeDelta() const { return mRealtime.delta; } + + /** Get the real-time time in seconds. + */ + /** Tick the clock. Calling this function has no effect if the clock is paused + */ + Clock& tick(); + + /** Set the requested FPS to simulate, or disable FPS simulation. + When enabling FPS simulation, calls to tick() will change the time by `1/FPS` seconds. + If FPS simulation is disabled, calling `tick()` will add the actual time that passed since the previous `tick()` call + */ + Clock& framerate(uint32_t fps); + + /** Get the requested FPS value + */ + uint32_t framerate() const { return mFramerate; } + + /** Pause the clock + */ + Clock& pause() { mPaused = true; return *this; } + + /** Resume the clock + */ + Clock& play(); + + /** Stop the clock (pause + reset) + */ + Clock& stop() { now(0); return pause(); } + + /** Step forward or backward. Ignored if the Clock is running or not in FPS simulation mode + \param[in] frames The number of frames to step. Can be negative + The function will not step backward beyond frame zero + */ + Clock& step(int64_t frames = 1); + + /** Set the time scale. This value is ignored when simulating FPS + */ + Clock& timeScale(double scale) { mScale = scale; return *this; } + + /** Get the scale + */ + double timeScale() const { return mScale; } + + /** Check if the clock is paused + */ + bool isPaused() const { return mPaused; } + + /** Check if the clock is in real-time mode + */ + bool simulatingFps() const { return mFramerate != 0; } + + /** Render the UI + */ + void renderUI(Gui::Window& w); + + /** Get the script string + */ + std::string getScript(const std::string& var) const; + private: + struct Time + { + double now = 0; + double delta = 0; + void update(double time) + { + delta = time - now; + now = time; + } + } mRealtime, mTime; + + uint32_t mFramerate = 0; + uint64_t mFrames = 0; + uint64_t mTicksPerFrame = 0; + CpuTimer mTimer; + + bool mPaused = false; + double mScale = 1; + std::optional mDeferredTime; + std::optional mDeferredFrameID; + + void updateTimer(); + void resetDeferredObjects(); + }; +} diff --git a/Framework/Source/Utils/CpuTimer.h b/Source/Falcor/Utils/Timing/CpuTimer.h similarity index 91% rename from Framework/Source/Utils/CpuTimer.h rename to Source/Falcor/Utils/Timing/CpuTimer.h index 590b5479f..c56d0db5d 100644 --- a/Framework/Source/Utils/CpuTimer.h +++ b/Source/Falcor/Utils/Timing/CpuTimer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -58,22 +58,22 @@ namespace Falcor /** Get the time that passed from the last update() call to the one before that. */ - float getElapsedTime() const + double delta() const { - return float(mElapsedTime.count()); + return mElapsedTime.count(); } /** Calculate the duration in milliseconds between 2 time points */ - static float calcDuration(TimePoint start, TimePoint end) + static double calcDuration(TimePoint start, TimePoint end) { auto delta = end.time_since_epoch() - start.time_since_epoch(); auto duration = std::chrono::duration_cast(delta); - return ((float)duration.count()) * 1.0e-6f; + return duration.count() * 1.0e-6; } private: TimePoint mCurrentTime; std::chrono::duration mElapsedTime; }; -} \ No newline at end of file +} diff --git a/Framework/Source/API/D3D12/D3DProgramVersion.cpp b/Source/Falcor/Utils/Timing/FrameRate.cpp similarity index 76% rename from Framework/Source/API/D3D12/D3DProgramVersion.cpp rename to Source/Falcor/Utils/Timing/FrameRate.cpp index 6ad5b5bff..ec6a1009f 100644 --- a/Framework/Source/API/D3D12/D3DProgramVersion.cpp +++ b/Source/Falcor/Utils/Timing/FrameRate.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,21 +25,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Graphics/Program/ProgramVersion.h" -#include "Graphics/Program/ProgramReflection.h" -#include -#include +#include "stdafx.h" +#include "FrameRate.h" +#include namespace Falcor { - void ProgramVersion::deleteApiHandle() + std::string FrameRate::getMsg(bool vsyncOn) const { - - } - - bool ProgramVersion::init(std::string& log) - { - return true; + float msPerFrame = (float)getAverageFrameTime(); + std::stringstream strstr; + std::string msStr = std::to_string(msPerFrame); + std::string s = std::to_string(int(ceil(1000 / msPerFrame))) + " FPS (" + msStr.erase(msStr.size() - 4) + " ms/frame)"; + if (vsyncOn) s += std::string(", VSync"); + return s; } } diff --git a/Framework/Source/Utils/FrameRate.h b/Source/Falcor/Utils/Timing/FrameRate.h similarity index 75% rename from Framework/Source/Utils/FrameRate.h rename to Source/Falcor/Utils/Timing/FrameRate.h index 2f9943c75..0dff63350 100644 --- a/Framework/Source/Utils/FrameRate.h +++ b/Source/Falcor/Utils/Timing/FrameRate.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,30 +26,29 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include #include -#include "CpuTimer.h" +#include "Clock.h" namespace Falcor { /** Framerate calculator */ - class FrameRate + class dlldecl FrameRate { public: FrameRate() { mFrameTimes.resize(sFrameWindow); - resetClock(); + reset(); } - /** Resets the calculator. + /** Resets the FPS After this call it will appear as if the application had just started. Useful in cases a new scene is loaded, since it will display a more accurate FPS. */ - void resetClock() + void reset() { - newFrame(); mFrameCount = 0; + mClock.now(0).tick(); } /** Tick the timer. @@ -58,43 +57,38 @@ namespace Falcor void newFrame() { mFrameCount++; - mTimer.update(); - mFrameTimes[mFrameCount % sFrameWindow] = mTimer.getElapsedTime(); + mFrameTimes[mFrameCount % sFrameWindow] = mClock.tick().realTimeDelta(); + mClock.now(0).tick(); } /** Get the time in ms it took to render a frame */ - float getAverageFrameTime() const + double getAverageFrameTime() const { uint64_t frames = min(mFrameCount, sFrameWindow); double elapsedTime = 0; - for(uint64_t i = 0; i < frames; i++) - { - elapsedTime += mFrameTimes[i]; - } - + for(uint64_t i = 0; i < frames; i++) elapsedTime += mFrameTimes[i]; double time = elapsedTime / double(frames) * 1000; - return float(time); + return time; } - /** Get the time that passed from the last NewFrame() call to the one before that. + /** Get the time that it took to render the last frame */ - float getLastFrameTime() const + double getLastFrameTime() const { return mFrameTimes[mFrameCount % sFrameWindow]; } - /** Get the numer of frames passed from the last resetClock() call. + /** Get a message with the FPS */ - uint64_t getFrameCount() const - { - return mFrameCount; - } - private: + std::string getMsg(bool vsyncOn = false) const; - CpuTimer mTimer; - std::vector mFrameTimes; + private: + Clock mClock; + std::vector mFrameTimes; uint64_t mFrameCount; static const uint64_t sFrameWindow = 60; }; -} \ No newline at end of file + + inline std::string to_string(const FrameRate& fr, bool vsyncOn = false) { return fr.getMsg(vsyncOn); } +} diff --git a/Framework/Source/Utils/Profiler.cpp b/Source/Falcor/Utils/Timing/Profiler.cpp similarity index 54% rename from Framework/Source/Utils/Profiler.cpp rename to Source/Falcor/Utils/Timing/Profiler.cpp index c4a88a898..5a4ad486a 100644 --- a/Framework/Source/Utils/Profiler.cpp +++ b/Source/Falcor/Utils/Timing/Profiler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,29 +25,29 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Profiler.h" -#include "API/GpuTimer.h" -#include "API/LowLevel/FencedPool.h" - -#include -#include +#include "Core/API/GpuTimer.h" +#include "Core/API/FencedPool.h" #include -#include +#include +#define USE_PIX +#include "WinPixEventRuntime/Include/WinPixEventRuntime/pix3.h" namespace Falcor { bool gProfileEnabled = false; - std::map Profiler::sProfilerEvents; + std::unordered_map Profiler::sProfilerEvents; + std::vector Profiler::sRegisteredEvents; + std::string curEventName = ""; uint32_t Profiler::sCurrentLevel = 0; uint32_t Profiler::sGpuTimerIndex = 0; - std::vector Profiler::sProfilerVector; void Profiler::initNewEvent(EventData *pEvent, const std::string& name) { pEvent->name = name; - sProfilerEvents[name] = pEvent; + sProfilerEvents[curEventName] = pEvent; } Profiler::EventData* Profiler::createNewEvent(const std::string& name) @@ -69,44 +69,66 @@ namespace Falcor return event ? event : createNewEvent(name); } - void Profiler::startEvent(const std::string& name, bool showInMsg) + void Profiler::startEvent(const std::string& name, Flags flags, bool showInMsg) { - EventData* pData = getEvent(name); - pData->triggered++; - if (pData->triggered > 1) + if (is_set(flags, Flags::Internal)) { - logWarning("Profiler event `" + name + "` was triggered while it is already running. Nesting profiler events with the same name is disallowed and you should probably fix that. Ignoring the new call"); - return; - } + curEventName = curEventName + "#" + name; + EventData* pData = getEvent(curEventName); + pData->triggered++; + if (pData->triggered > 1) + { + logWarning("Profiler event `" + name + "` was triggered while it is already running. Nesting profiler events with the same name is disallowed and you should probably fix that. Ignoring the new call"); + return; + } + + pData->showInMsg = showInMsg; + pData->level = sCurrentLevel; + pData->cpuStart = CpuTimer::getCurrentTimePoint(); + EventData::FrameData& frame = pData->frameData[sGpuTimerIndex]; + if (frame.currentTimer >= frame.pTimers.size()) + { + frame.pTimers.push_back(GpuTimer::create()); + } + frame.pTimers[frame.currentTimer]->begin(); + pData->callStack.push(frame.currentTimer); + frame.currentTimer++; + sCurrentLevel++; - sProfilerVector.push_back(pData); - pData->showInMsg = showInMsg; - pData->level = sCurrentLevel; - pData->cpuStart = CpuTimer::getCurrentTimePoint(); - EventData::FrameData& frame = pData->frameData[sGpuTimerIndex]; - if (frame.currentTimer >= frame.pTimers.size()) + if (!pData->registered) + { + sRegisteredEvents.push_back(pData); + pData->registered = true; + } + } + if (is_set(flags, Flags::Pix)) { - frame.pTimers.push_back(GpuTimer::create()); + PIXBeginEvent((ID3D12GraphicsCommandList*)gpDevice->getRenderContext()->getLowLevelData()->getCommandList(), PIX_COLOR(0, 0, 0), name.c_str()); } - frame.pTimers[frame.currentTimer]->begin(); - pData->callStack.push(frame.currentTimer); - frame.currentTimer++; - sCurrentLevel++; } - void Profiler::endEvent(const std::string& name) + void Profiler::endEvent(const std::string& name, Flags flags) { - EventData* pData = getEvent(name); - pData->triggered--; - if (pData->triggered != 0) return; + if (is_set(flags, Flags::Internal)) + { + assert(isEventRegistered(curEventName)); + EventData* pData = getEvent(curEventName); + pData->triggered--; + if (pData->triggered != 0) return; - pData->cpuEnd = CpuTimer::getCurrentTimePoint(); - pData->cpuTotal += CpuTimer::calcDuration(pData->cpuStart, pData->cpuEnd); + pData->cpuEnd = CpuTimer::getCurrentTimePoint(); + pData->cpuTotal += CpuTimer::calcDuration(pData->cpuStart, pData->cpuEnd); - pData->frameData[sGpuTimerIndex].pTimers[pData->callStack.top()]->end(); - pData->callStack.pop(); + pData->frameData[sGpuTimerIndex].pTimers[pData->callStack.top()]->end(); + pData->callStack.pop(); - sCurrentLevel--; + sCurrentLevel--; + curEventName.erase(curEventName.find_last_of("#")); + } + if (is_set(flags, Flags::Pix)) + { + PIXEndEvent((ID3D12GraphicsCommandList*)gpDevice->getRenderContext()->getLowLevelData()->getCommandList()); + } } double Profiler::getEventGpuTime(const std::string& name) @@ -138,22 +160,23 @@ namespace Falcor std::string Profiler::getEventsString() { - std::string results("Name\t\t\t\t\tCPU time(ms)\tGPU time(ms)\n"); + std::string results("Name\t\t\t\t\tCPU time(ms)\t\t GPU time(ms)\n"); - for (EventData* pData : sProfilerVector) + for (EventData* pData : sRegisteredEvents) { assert(pData->triggered == 0); - if(pData->showInMsg == false) continue; + if (pData->showInMsg == false) continue; double gpuTime = getGpuTime(pData); assert(pData->callStack.empty()); char event[1000]; uint32_t nameIndent = pData->level * 2 + 1; - uint32_t cpuIndent = 30 - (nameIndent + (uint32_t)pData->name.size()); - snprintf(event, 1000, "%*s%s %*.2f %14.2f\n", nameIndent, " ", pData->name.c_str(), cpuIndent, getCpuTime(pData), gpuTime); + uint32_t cpuIndent = 30 - (nameIndent + (uint32_t)pData->name.substr(pData->name.find_last_of("#") + 1).size()); + snprintf(event, 1000, "%*s%s %*.2f (%.2f) %14.2f (%.2f)\n", nameIndent, " ", pData->name.substr(pData->name.find_last_of("#") + 1).c_str(), cpuIndent, getCpuTime(pData), + pData->cpuRunningAverageMS, gpuTime, pData->gpuRunningAverageMS); #if _PROFILING_LOG == 1 - pData->cpuMs[pData->stepNr] = pData->cpuTotal; + pData->cpuMs[pData->stepNr] = (float)pData->cpuTotal; pData->gpuMs[pData->stepNr] = (float)gpuTime; pData->stepNr++; if (pData->stepNr == _PROFILING_LOG_BATCH_SIZE) @@ -178,20 +201,34 @@ namespace Falcor void Profiler::endFrame() { - for (EventData* pData : sProfilerVector) + for (EventData* pData : sRegisteredEvents) { + // Update CPU/GPU time running averages. + const double cpuTime = getCpuTime(pData); + const double gpuTime = getGpuTime(pData); + // With sigma = 0.98, then after 100 frames, a given value's contribution is down to ~1.7% of + // the running average, which seems to provide a reasonable trade-off of temporal smoothing + // versus setting in to a new value when something has changed. + const double sigma = .98; + if (pData->cpuRunningAverageMS < 0.) pData->cpuRunningAverageMS = cpuTime; + else pData->cpuRunningAverageMS = sigma * pData->cpuRunningAverageMS + (1. - sigma) * cpuTime; + if (pData->gpuRunningAverageMS < 0.) pData->gpuRunningAverageMS = gpuTime; + else pData->gpuRunningAverageMS = sigma * pData->gpuRunningAverageMS + (1. - sigma) * gpuTime; + pData->showInMsg = false; pData->cpuTotal = 0; - pData->triggered = 0; + pData->triggered = 0; pData->frameData[1 - sGpuTimerIndex].currentTimer = 0; + pData->registered = false; } - sProfilerVector.clear(); + sRegisteredEvents.clear(); sGpuTimerIndex = 1 - sGpuTimerIndex; } #if _PROFILING_LOG == 1 - void Profiler::flushLog() { - for (EventData* pData : sProfilerVector) + void Profiler::flushLog() + { + for (EventData* pData : sRegisteredEvents) { std::ostringstream logOss, fileOss; logOss << "dumping " << "profile_" << pData->name << "_" << pData->filesWritten; @@ -209,13 +246,14 @@ namespace Falcor void Profiler::clearEvents() { - for (EventData* pData : sProfilerVector) + for (EventData* pData : sRegisteredEvents) { delete pData; } sProfilerEvents.clear(); - sProfilerVector.clear(); + sRegisteredEvents.clear(); sCurrentLevel = 0; sGpuTimerIndex = 0; + curEventName = ""; } } diff --git a/Framework/Source/Utils/Profiler.h b/Source/Falcor/Utils/Timing/Profiler.h similarity index 79% rename from Framework/Source/Utils/Profiler.h rename to Source/Falcor/Utils/Timing/Profiler.h index 5e978ec1d..83559afb5 100644 --- a/Framework/Source/Utils/Profiler.h +++ b/Source/Falcor/Utils/Timing/Profiler.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,18 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include -#include -#include -#include "API/GpuTimer.h" -#include "Utils/CpuTimer.h" -#include "FalcorConfig.h" #include +#include +#include "CpuTimer.h" +#include "Core/API/GpuTimer.h" namespace Falcor { - extern bool gProfileEnabled; + extern dlldecl bool gProfileEnabled; class GpuTimer; @@ -46,7 +42,7 @@ namespace Falcor This class uses a double-buffering scheme for GPU profiling to avoid GPU stalls. ProfilerEvent is a wrapper class which together with scoping can simplify event profiling. */ - class Profiler + class dlldecl Profiler { public: @@ -54,6 +50,15 @@ namespace Falcor static void flushLog(); #endif + enum class Flags + { + None = 0x0, + Internal = 0x1, + Pix = 0x2, + + Default = Internal | Pix + }; + struct EventData { virtual ~EventData() {} @@ -68,9 +73,12 @@ namespace Falcor std::stack callStack; CpuTimer::TimePoint cpuStart; CpuTimer::TimePoint cpuEnd; - float cpuTotal = 0; + double cpuTotal = 0; + double cpuRunningAverageMS = -1.f; // Negative value to signify invalid + double gpuRunningAverageMS = -1.f; uint32_t level; uint32_t triggered = 0; + bool registered = false; #if _PROFILING_LOG == 1 int stepNr = 0; int filesWritten = 0; @@ -82,12 +90,12 @@ namespace Falcor /** Start profiling a new event and update the events hierarchies. \param[in] name The event name. */ - static void startEvent(const std::string& name, bool showInMsg = true); + static void startEvent(const std::string& name, Flags flags = Flags::Default, bool showInMsg = true); /** Finish profiling a new event and update the events hierarchies. \param[in] name The event name. */ - static void endEvent(const std::string& name); + static void endEvent(const std::string& name, Flags flags = Flags::Default); /** Finish profiling for the entire frame. Due to the double-buffering nature of the profiler, the results returned are for the previous frame. @@ -103,7 +111,7 @@ namespace Falcor \param[in] name The event name. */ static EventData* createNewEvent(const std::string& name); - + /** Initialize a previously generated event. Used to do the default initialization without creating the actual event instance, to support derived event types. See \ref Cuda::Profiler::EventData. \param[out] pEvent Event to initialize @@ -131,7 +139,7 @@ namespace Falcor */ static EventData* isEventRegistered(const std::string& name); - /** Clears all the events. + /** Clears all the events. Useful if you want to start profiling a different technique with different events. */ static void clearEvents(); @@ -140,8 +148,8 @@ namespace Falcor static double getGpuTime(const EventData* pData); static double getCpuTime(const EventData* pData); - static std::map sProfilerEvents; - static std::vector sProfilerVector; + static std::unordered_map sProfilerEvents; + static std::vector sRegisteredEvents; static uint32_t sCurrentLevel; static uint32_t sGpuTimerIndex; }; @@ -155,18 +163,25 @@ namespace Falcor public: /** C'tor */ - ProfilerEvent(const std::string& name) : mName(name) { if(gProfileEnabled) { Profiler::startEvent(name); } } + ProfilerEvent(const std::string& name, Profiler::Flags flags = Profiler::Flags::Default) : mName(name), mFlags(flags) { if (gProfileEnabled) { Profiler::startEvent(name, flags); } } /** D'tor */ - ~ProfilerEvent() { if(gProfileEnabled) {Profiler::endEvent(mName); }} + ~ProfilerEvent() { if (gProfileEnabled) { Profiler::endEvent(mName, mFlags); } } private: const std::string mName; + Profiler::Flags mFlags; }; #if _PROFILING_ENABLED -#define PROFILE(_name) Falcor::ProfilerEvent _profileEvent##__LINE__(_name); +#define PROFILE_ALL_FLAGS(_name) Falcor::ProfilerEvent _profileEvent##__LINE__(_name) +#define PROFILE_SOME_FLAGS(_name, _flags) Falcor::ProfilerEvent _profileEvent##__LINE__(_name, _flags) + +#define GET_PROFILE(_1, _2, NAME, ...) NAME +#define PROFILE(...) GET_PROFILE(__VA_ARGS__, PROFILE_SOME_FLAGS, PROFILE_ALL_FLAGS)(__VA_ARGS__) #else #define PROFILE(_name) #endif + + enum_class_operators(Profiler::Flags); } diff --git a/Framework/Source/Utils/DebugDrawer.cpp b/Source/Falcor/Utils/UI/DebugDrawer.cpp similarity index 51% rename from Framework/Source/Utils/DebugDrawer.cpp rename to Source/Falcor/Utils/UI/DebugDrawer.cpp index 6002d3f10..531ff8945 100644 --- a/Framework/Source/Utils/DebugDrawer.cpp +++ b/Source/Falcor/Utils/UI/DebugDrawer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "Utils/DebugDrawer.h" -#include "API/RenderContext.h" -#include "Graphics/Camera/Camera.h" -#include "Utils/AABB.h" -#include +#include "stdafx.h" +#include "DebugDrawer.h" +#include "Utils/Math/AABB.h" +#include "Core/API/RenderContext.h" +#include "Scene/Camera/Camera.h" namespace Falcor { @@ -93,108 +92,104 @@ namespace Falcor } // Generates a quad centered at currFrame's position facing nextFrame's position - DebugDrawer::Quad createQuadForFrame(const ObjectPath::Frame& currFrame, const ObjectPath::Frame& nextFrame) +// DebugDrawer::Quad createQuadForFrame(const ObjectPath::Frame& currFrame, const ObjectPath::Frame& nextFrame) +// { +// glm::vec3 forward = nextFrame.position - currFrame.position; +// glm::vec3 right = glm::cross(forward, currFrame.up); +// glm::vec3 up = glm::cross(right, forward); +// +// return buildQuad(currFrame.position, up, right); +// } + +// // Generates a quad centered at currFrame's position oriented halfway between direction to prevFrame and direction to nextFrame +// DebugDrawer::Quad createQuadForFrame(const ObjectPath::Frame& prevFrame, const ObjectPath::Frame& currFrame, const ObjectPath::Frame& nextFrame) +// { +// glm::vec3 lastToCurrFoward = currFrame.position - prevFrame.position; +// glm::vec3 lastToCurrRight = glm::normalize(glm::cross(lastToCurrFoward, prevFrame.up)); +// glm::vec3 lastToCurrUp = glm::normalize(glm::cross(lastToCurrRight, lastToCurrFoward)); +// +// glm::vec3 currToNextFoward = nextFrame.position - currFrame.position; +// +// // If curr and next are the same, use the direction from prev to curr +// if (glm::length(currToNextFoward) < 0.001f) +// { +// currToNextFoward = lastToCurrFoward; +// } +// +// glm::vec3 currToNextRight = glm::normalize(glm::cross(currToNextFoward, currFrame.up)); +// glm::vec3 currToNextUp = glm::normalize(glm::cross(currToNextRight, currToNextFoward)); +// +// // Half vector between two direction normals +// glm::vec3 midUp = (lastToCurrUp + currToNextUp) / 2.0f; +// glm::vec3 midRight = (lastToCurrRight + currToNextRight) / 2.0f; +// +// return buildQuad(currFrame.position, midUp, midRight); +// } + +// void DebugDrawer::addPath(const ObjectPath::SharedPtr& pPath) +// { +// // If a path has one or no keyframes, there's no path to draw +// if (pPath->getKeyFrameCount() <= 1) +// { +// return; +// } +// +// const float step = 1.0f / (float)kPathDetail; +// const float epsilon = 1.0e-6f; // A bit more than glm::epsilon +// +// ObjectPath::Frame prevFrame; +// pPath->getFrameAt(0, 0.0f, prevFrame); +// +// ObjectPath::Frame currFrame; +// pPath->getFrameAt(0, step, currFrame); +// +// Quad lastQuad = createQuadForFrame(prevFrame, currFrame); +// Quad currQuad; +// +// // Draw quad to cap path beginning +// addQuad(lastQuad); +// +// const float maxFrameIndex = (float)(pPath->getKeyFrameCount() - 1); +// +// // Add epsilon so loop's <= works properly +// const float pathEnd = maxFrameIndex + epsilon; +// +// for (float frame = step; frame <= pathEnd; frame += step) +// { +// // Loop can overshoot the max index +// // Clamp frame to right below max index so interpolation on the path will work +// frame = std::min(frame, maxFrameIndex - epsilon); +// +// uint32_t frameID = (uint32_t)(glm::floor(frame)); +// float t = frame - (float)frameID; +// +// ObjectPath::Frame nextFrame; +// pPath->getFrameAt(frameID, t + step, nextFrame); +// currQuad = createQuadForFrame(prevFrame, currFrame, nextFrame); +// +// // Draw current quad +// addQuad(currQuad); +// +// // Connect last quad to current +// addLine(lastQuad[0], currQuad[0]); +// addLine(lastQuad[1], currQuad[1]); +// addLine(lastQuad[2], currQuad[2]); +// addLine(lastQuad[3], currQuad[3]); +// +// prevFrame = currFrame; +// lastQuad = currQuad; +// currFrame = nextFrame; +// } +// } + + void DebugDrawer::render(RenderContext* pContext, GraphicsState* pState, GraphicsVars* pVars, Camera *pCamera) { - glm::vec3 forward = nextFrame.position - currFrame.position; - glm::vec3 right = glm::cross(forward, currFrame.up); - glm::vec3 up = glm::cross(right, forward); - - return buildQuad(currFrame.position, up, right); - } - - // Generates a quad centered at currFrame's position oriented halfway between direction to prevFrame and direction to nextFrame - DebugDrawer::Quad createQuadForFrame(const ObjectPath::Frame& prevFrame, const ObjectPath::Frame& currFrame, const ObjectPath::Frame& nextFrame) - { - glm::vec3 lastToCurrFoward = currFrame.position - prevFrame.position; - glm::vec3 lastToCurrRight = glm::normalize(glm::cross(lastToCurrFoward, prevFrame.up)); - glm::vec3 lastToCurrUp = glm::normalize(glm::cross(lastToCurrRight, lastToCurrFoward)); - - glm::vec3 currToNextFoward = nextFrame.position - currFrame.position; - - // If curr and next are the same, use the direction from prev to curr - if (glm::length(currToNextFoward) < 0.001f) - { - currToNextFoward = lastToCurrFoward; - } - - glm::vec3 currToNextRight = glm::normalize(glm::cross(currToNextFoward, currFrame.up)); - glm::vec3 currToNextUp = glm::normalize(glm::cross(currToNextRight, currToNextFoward)); - - // Half vector between two direction normals - glm::vec3 midUp = (lastToCurrUp + currToNextUp) / 2.0f; - glm::vec3 midRight = (lastToCurrRight + currToNextRight) / 2.0f; - - return buildQuad(currFrame.position, midUp, midRight); - } - - void DebugDrawer::addPath(const ObjectPath::SharedPtr& pPath) - { - // If a path has one or no keyframes, there's no path to draw - if (pPath->getKeyFrameCount() <= 1) - { - return; - } - - const float step = 1.0f / (float)kPathDetail; - const float epsilon = 1.0e-6f; // A bit more than glm::epsilon - - ObjectPath::Frame prevFrame; - pPath->getFrameAt(0, 0.0f, prevFrame); - - ObjectPath::Frame currFrame; - pPath->getFrameAt(0, step, currFrame); - - Quad lastQuad = createQuadForFrame(prevFrame, currFrame); - Quad currQuad; - - // Draw quad to cap path beginning - addQuad(lastQuad); - - const float maxFrameIndex = (float)(pPath->getKeyFrameCount() - 1); - - // Add epsilon so loop's <= works properly - const float pathEnd = maxFrameIndex + epsilon; - - for (float frame = step; frame <= pathEnd; frame += step) - { - // Loop can overshoot the max index - // Clamp frame to right below max index so interpolation on the path will work - frame = std::min(frame, maxFrameIndex - epsilon); - - uint32_t frameID = (uint32_t)(glm::floor(frame)); - float t = frame - (float)frameID; - - ObjectPath::Frame nextFrame; - pPath->getFrameAt(frameID, t + step, nextFrame); - currQuad = createQuadForFrame(prevFrame, currFrame, nextFrame); - - // Draw current quad - addQuad(currQuad); - - // Connect last quad to current - addLine(lastQuad[0], currQuad[0]); - addLine(lastQuad[1], currQuad[1]); - addLine(lastQuad[2], currQuad[2]); - addLine(lastQuad[3], currQuad[3]); - - prevFrame = currFrame; - lastQuad = currQuad; - currFrame = nextFrame; - } - } - - void DebugDrawer::render(RenderContext* pContext, Camera *pCamera) - { - ConstantBuffer* pCB = pContext->getGraphicsVars()->getConstantBuffer("InternalPerFrameCB").get(); - if (pCB != nullptr) - { - pCamera->setIntoConstantBuffer(pCB, 0); - } + ConstantBuffer* pCB = pVars->getConstantBuffer("InternalPerFrameCB").get(); + if (pCB != nullptr) pCamera->setIntoConstantBuffer(pCB, 0); uploadBuffer(); - pContext->getGraphicsState()->setVao(mpVao); - - pContext->draw((uint32_t)mVertexData.size(), 0); + pState->setVao(mpVao); + pContext->draw(pState, pVars, (uint32_t)mVertexData.size(), 0); } void DebugDrawer::uploadBuffer() @@ -202,7 +197,7 @@ namespace Falcor if (mDirty) { auto pVertexBuffer = mpVao->getVertexBuffer(0); - pVertexBuffer->updateData(mVertexData.data(), 0, sizeof(LineVertex) * mVertexData.size()); + pVertexBuffer->setBlob(mVertexData.data(), 0, sizeof(LineVertex) * mVertexData.size()); mDirty = false; } } diff --git a/Framework/Source/Utils/DebugDrawer.h b/Source/Falcor/Utils/UI/DebugDrawer.h similarity index 91% rename from Framework/Source/Utils/DebugDrawer.h rename to Source/Falcor/Utils/UI/DebugDrawer.h index f30f2f648..cfc181433 100644 --- a/Framework/Source/Utils/DebugDrawer.h +++ b/Source/Falcor/Utils/UI/DebugDrawer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,19 +26,19 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once - -#include "Graphics/Paths/ObjectPath.h" -#include "API/VAO.h" +#include "Core/API/VAO.h" namespace Falcor { + struct BoundingBox; class RenderContext; class Camera; - struct BoundingBox; + class GraphicsState; + class GraphicsVars; /** Utility class to assist in drawing debug geometry */ - class DebugDrawer + class dlldecl DebugDrawer { public: @@ -68,13 +68,9 @@ namespace Falcor */ void addBoundingBox(const BoundingBox& aabb); - /** Adds a path visualized as a four-sided "tube" - */ - void addPath(const ObjectPath::SharedPtr& pPath); - /** Renders the contents of the debug drawer */ - void render(RenderContext* pContext, Camera *pCamera); + void render(RenderContext* pContext, GraphicsState* pState, GraphicsVars* pVars, Camera *pCamera); /** Get how many vertices are currently pushed */ diff --git a/Framework/Source/Utils/Font.cpp b/Source/Falcor/Utils/UI/Font.cpp similarity index 94% rename from Framework/Source/Utils/Font.cpp rename to Source/Falcor/Utils/UI/Font.cpp index 0f867dc19..a91884f49 100644 --- a/Framework/Source/Utils/Font.cpp +++ b/Source/Falcor/Utils/UI/Font.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,12 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "Font.h" -#include "Utils/Platform/OS.h" +#include "Core/API/Texture.h" #include -#include "Graphics/TextureHelper.h" -#include "API/Texture.h" namespace Falcor { @@ -130,7 +128,7 @@ namespace Falcor } // Load the texture - mpTexture = createTextureFromFile(TextureFilename, false, false); + mpTexture = Texture::createFromFile(TextureFilename, false, false); return true; } -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Font.h b/Source/Falcor/Utils/UI/Font.h similarity index 96% rename from Framework/Source/Utils/Font.h rename to Source/Falcor/Utils/UI/Font.h index 2076a860d..e98718f74 100644 --- a/Framework/Source/Utils/Font.h +++ b/Source/Falcor/Utils/UI/Font.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,9 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec2.hpp" -#include -#include "API/Texture.h" +#include "Core/API/Texture.h" namespace Falcor { @@ -94,4 +92,4 @@ namespace Falcor float mTabWidth; float mLetterSpacing; }; -} \ No newline at end of file +} diff --git a/Framework/Source/Graphics/Scene/Editor/Gizmo.cpp b/Source/Falcor/Utils/UI/Gizmo.cpp similarity index 98% rename from Framework/Source/Graphics/Scene/Editor/Gizmo.cpp rename to Source/Falcor/Utils/UI/Gizmo.cpp index 6264a9a75..664518d12 100644 --- a/Framework/Source/Graphics/Scene/Editor/Gizmo.cpp +++ b/Source/Falcor/Utils/UI/Gizmo.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,17 +25,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Graphics/Scene/Editor/Gizmo.h" +#include "stdafx.h" +#include "Utils/UI/Gizmo.h" +#include "Utils/UI/UserInput.h" #include "glm/gtx/intersect.hpp" #include "glm/gtx/matrix_interpolation.hpp" -#include "Utils/Math/FalcorMath.h" -#include #include "glm/gtx/projection.hpp" namespace Falcor { +#if 0 const std::array Gizmo::kAxes = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; const float Gizmo::kGizmoSizeScale = 0.06f; @@ -305,5 +304,5 @@ namespace Falcor glm::vec3 modelScaleDelta = kAxes[(uint32_t)mTransformAxis] * distance; pInstance->setScaling(pInstance->getScaling() + modelScaleDelta); } - +#endif } diff --git a/Framework/Source/Graphics/Scene/Editor/Gizmo.h b/Source/Falcor/Utils/UI/Gizmo.h similarity index 94% rename from Framework/Source/Graphics/Scene/Editor/Gizmo.h rename to Source/Falcor/Utils/UI/Gizmo.h index 7a63b119d..baa5b2add 100644 --- a/Framework/Source/Graphics/Scene/Editor/Gizmo.h +++ b/Source/Falcor/Utils/UI/Gizmo.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - #pragma once - -#include "Graphics/Scene/Scene.h" -#include "Graphics/Camera/Camera.h" +#include "Scene/Scene.h" #include +#if 0 namespace Falcor { + struct MouseEvent; + /** Used by the Scene Editor to allow users to modify object transforms through mouse interactions. */ - class Gizmo + class dlldecl Gizmo { public: using SharedPtr = std::shared_ptr; @@ -147,7 +147,7 @@ namespace Falcor }; - class TranslateGizmo : public Gizmo, public inherit_shared_from_this + class dlldecl TranslateGizmo : public Gizmo { public: using SharedPtr = std::shared_ptr; @@ -167,7 +167,7 @@ namespace Falcor }; - class RotateGizmo : public Gizmo, public inherit_shared_from_this + class dlldecl RotateGizmo : public Gizmo { public: using SharedPtr = std::shared_ptr; @@ -177,7 +177,7 @@ namespace Falcor virtual void applyDelta(const Scene::ModelInstance::SharedPtr& pInstance) const override; virtual void applyDelta(const Camera::SharedPtr& pCamera) const override; - virtual void applyDelta(const PointLight::SharedPtr& pLight) const override; + virtual void applyDelta(const PointLight::SharedPtr & pLight) const override; private: virtual void findBestPlane(const Camera::SharedPtr& pCamera) override; @@ -189,7 +189,7 @@ namespace Falcor }; - class ScaleGizmo : public Gizmo, public inherit_shared_from_this + class dlldecl ScaleGizmo : public Gizmo { public: using SharedPtr = std::shared_ptr; @@ -212,3 +212,4 @@ namespace Falcor } +#endif diff --git a/Source/Falcor/Utils/UI/Gui.cpp b/Source/Falcor/Utils/UI/Gui.cpp new file mode 100644 index 000000000..745951526 --- /dev/null +++ b/Source/Falcor/Utils/UI/Gui.cpp @@ -0,0 +1,1463 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Gui.h" +#include "dear_imgui/imgui.h" +#include "UserInput.h" +#include "Core/API/RenderContext.h" +#include "glm/gtc/type_ptr.hpp" +#include "Utils/StringUtils.h" + +#pragma warning (disable : 4756) // overflow in constant arithmetic caused by calculating the setFloat*() functions (when calculating the step and min/max are +/- INF) +namespace Falcor +{ + class GuiImpl + { + public: + GuiImpl() = default; + + private: + friend class Gui; + void init(Gui* pGui, float scaleFactor); + void createVao(uint32_t vertexCount, uint32_t indexCount); + void compileFonts(); + + // Helper to create multiple inline text boxes + bool addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine); + + struct ComboData + { + uint32_t lastVal = -1; + int32_t currentItem = -1; + }; + std::unordered_map mDropDownValues; + + // This struct is used to cache the mouse events + struct MouseEvents + { + bool buttonPressed[3] = { 0 }; + bool buttonReleased[3] = { 0 }; + }; + + MouseEvents mMouseEvents; + void setIoMouseEvents(); + void resetMouseEvents(); + + Vao::SharedPtr mpVao; + VertexLayout::SharedPtr mpLayout; + GraphicsState::SharedPtr mpPipelineState; + uint32_t mGroupStackSize = 0; + + GraphicsProgram::SharedPtr mpProgram; + GraphicsVars::SharedPtr mpProgramVars; + std::vector mpImages; + ParameterBlockReflection::BindLocation mGuiImageLoc; + float mScaleFactor = 1.0f; + std::unordered_map mFontMap; + ImFont* mpActiveFont = nullptr; + + bool beginMenu(const char* name); + void endMenu(); + bool beginMainMenuBar(); + void endMainMenuBar(); + bool beginDropDownMenu(const char label[]); + void endDropDownMenu(); + bool addMenuItem(const char label[], bool& var, const char shortcut[] = nullptr); + bool addMenuItem(const char label[], const char shortcut[] = nullptr); + + bool pushWindow(const char label[], bool& open, uvec2 size = { 250, 200 }, uvec2 pos = { 20, 40 }, Gui::WindowFlags flags = Gui::WindowFlags::Default); + void popWindow(); + void setCurrentWindowPos(uint32_t x, uint32_t y); + void setCurrentWindowSize(uint32_t width, uint32_t height); + void beginColumns(uint32_t numColumns); + void nextColumn(); + + bool beginGroup(const char label[], bool beginExpanded = false); + bool beginGroup(const std::string& label, bool beginExpanded = false) { return beginGroup(label.c_str(), beginExpanded); } + void endGroup(); + + void indent(float i); + void addSeparator(uint32_t count = 1); + void addDummyItem(const char label[], const glm::vec2& size, bool sameLine = false); + void addRect(const glm::vec2& size, const glm::vec4& color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), bool filled = false, bool sameLine = false); + bool addDropdown(const char label[], const Gui::DropdownList& values, uint32_t& var, bool sameLine = false); + bool addButton(const char label[], bool sameLine = false); + bool addRadioButtons(const Gui::RadioButtonGroup& buttons, uint32_t& activeID); + bool addDirectionWidget(const char label[], glm::vec3& direction); + bool addCheckbox(const char label[], bool& var, bool sameLine = false); + bool addCheckbox(const char label[], int& var, bool sameLine = false); + template + bool addBoolVecVar(const char label[], T& var, bool sameLine = false); + bool addDragDropSource(const char label[], const char dataLabel[], const std::string& payloadString); + bool addDragDropDest(const char dataLabel[], std::string& payloadString); + + void addText(const char text[], bool sameLine = false); + bool addTextbox(const char label[], std::string& text, uint32_t lineCount = 1, Gui::TextFlags flags = Gui::TextFlags::Empty); + bool addTextbox(const char label[], char buf[], size_t bufSize, uint32_t lineCount = 1, Gui::TextFlags flags = Gui::TextFlags::Empty); + bool addMultiTextbox(const char label[], const std::vector& textLabels, std::vector& textEntries); + void addTooltip(const char tip[], bool sameLine = true); + + bool addRgbColor(const char label[], glm::vec3& var, bool sameLine = false); + bool addRgbaColor(const char label[], glm::vec4& var, bool sameLine = false); + + void addImage(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size = vec2(0), bool maintainRatio = true, bool sameLine = false); + bool addImageButton(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio = true, bool sameLine = false); + + template + bool addScalarVar(const char label[], T& var, T minVal = std::numeric_limits::min(), T maxVal = std::numeric_limits::max(), float step = 1.0f, bool sameLine = false, const char* displayFormat = nullptr); + template + bool addScalarSlider(const char label[], T& var, T minVal = std::numeric_limits::min(), T maxVal = std::numeric_limits::max(), bool sameLine = false, const char* displayFormat = nullptr); + + template + bool addVecVar(const char label[], T& var, typename T::value_type minVal = std::numeric_limits::min(), typename T::value_type maxVal = std::numeric_limits::max(), float step = 1.0f, bool sameLine = false, const char* displayFormat = nullptr); + template + bool addVecSlider(const char label[], T& var, typename T::value_type minVal = std::numeric_limits::min(), typename T::value_type maxVal = std::numeric_limits::max(), bool sameLine = false, const char* displayFormat = nullptr); + + template + bool addMatrixVar(const char label[], MatrixType& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false); + + void addGraph(const char label[], Gui::GraphCallback func, void* pUserData, uint32_t sampleCount, int32_t sampleOffset, float yMin = FLT_MAX, float yMax = FLT_MAX, uint32_t width = 0, uint32_t height = 100); + }; + + void GuiImpl::init(Gui* pGui, float scaleFactor) + { + mScaleFactor = scaleFactor; + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = (uint32_t)KeyboardEvent::Key::Tab; + io.KeyMap[ImGuiKey_LeftArrow] = (uint32_t)KeyboardEvent::Key::Left; + io.KeyMap[ImGuiKey_RightArrow] = (uint32_t)KeyboardEvent::Key::Right; + io.KeyMap[ImGuiKey_UpArrow] = (uint32_t)KeyboardEvent::Key::Up; + io.KeyMap[ImGuiKey_DownArrow] = (uint32_t)KeyboardEvent::Key::Down; + io.KeyMap[ImGuiKey_PageUp] = (uint32_t)KeyboardEvent::Key::PageUp; + io.KeyMap[ImGuiKey_PageDown] = (uint32_t)KeyboardEvent::Key::PageDown; + io.KeyMap[ImGuiKey_Home] = (uint32_t)KeyboardEvent::Key::Home; + io.KeyMap[ImGuiKey_End] = (uint32_t)KeyboardEvent::Key::End; + io.KeyMap[ImGuiKey_Delete] = (uint32_t)KeyboardEvent::Key::Del; + io.KeyMap[ImGuiKey_Backspace] = (uint32_t)KeyboardEvent::Key::Backspace; + io.KeyMap[ImGuiKey_Enter] = (uint32_t)KeyboardEvent::Key::Enter; + io.KeyMap[ImGuiKey_Escape] = (uint32_t)KeyboardEvent::Key::Escape; + io.KeyMap[ImGuiKey_A] = (uint32_t)KeyboardEvent::Key::A; + io.KeyMap[ImGuiKey_C] = (uint32_t)KeyboardEvent::Key::C; + io.KeyMap[ImGuiKey_V] = (uint32_t)KeyboardEvent::Key::V; + io.KeyMap[ImGuiKey_X] = (uint32_t)KeyboardEvent::Key::X; + io.KeyMap[ImGuiKey_Y] = (uint32_t)KeyboardEvent::Key::Y; + io.KeyMap[ImGuiKey_Z] = (uint32_t)KeyboardEvent::Key::Z; + io.IniFilename = nullptr; + + ImGuiStyle& style = ImGui::GetStyle(); + style.Colors[ImGuiCol_WindowBg].w = 0.9f; + style.Colors[ImGuiCol_FrameBg].x *= 0.1f; + style.Colors[ImGuiCol_FrameBg].y *= 0.1f; + style.Colors[ImGuiCol_FrameBg].z *= 0.1f; + style.ScrollbarSize *= 0.7f; + + style.Colors[ImGuiCol_MenuBarBg] = style.Colors[ImGuiCol_WindowBg]; + style.ScaleAllSizes(scaleFactor); + + // Create the pipeline state cache + mpPipelineState = GraphicsState::create(); + + // Create the program + mpProgram = GraphicsProgram::createFromFile("Framework/Shaders/Gui.slang", "vs", "ps"); + mpProgramVars = GraphicsVars::create(mpProgram->getReflector()); + mpPipelineState->setProgram(mpProgram); + + // Add the default font + pGui->addFont("", "Framework/Fonts/trebucbd.ttf"); + pGui->setActiveFont(""); + + // Create the blend state + BlendState::Desc blendDesc; + blendDesc.setRtBlend(0, true).setRtParams(0, BlendState::BlendOp::Add, BlendState::BlendOp::Add, BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, BlendState::BlendFunc::Zero); + mpPipelineState->setBlendState(BlendState::create(blendDesc)); + + // Create the rasterizer state + RasterizerState::Desc rsDesc; + rsDesc.setFillMode(RasterizerState::FillMode::Solid).setCullMode(RasterizerState::CullMode::None).setScissorTest(true).setDepthClamp(false); + mpPipelineState->setRasterizerState(RasterizerState::create(rsDesc)); + + // Create the depth-stencil state + DepthStencilState::Desc dsDesc; + dsDesc.setDepthEnabled(false); + mpPipelineState->setDepthStencilState(DepthStencilState::create(dsDesc)); + + // Create the VAO + VertexBufferLayout::SharedPtr pBufLayout = VertexBufferLayout::create(); + pBufLayout->addElement("POSITION", offsetof(ImDrawVert, pos), ResourceFormat::RG32Float, 1, 0); + pBufLayout->addElement("TEXCOORD", offsetof(ImDrawVert, uv), ResourceFormat::RG32Float, 1, 1); + pBufLayout->addElement("COLOR", offsetof(ImDrawVert, col), ResourceFormat::RGBA8Unorm, 1, 2); + mpLayout = VertexLayout::create(); + mpLayout->addBufferLayout(0, pBufLayout); + + mGuiImageLoc = mpProgram->getReflector()->getDefaultParameterBlock()->getResourceBinding("guiImage"); + } + + void GuiImpl::createVao(uint32_t vertexCount, uint32_t indexCount) + { + static_assert(sizeof(ImDrawIdx) == sizeof(uint16_t), "ImDrawIdx expected size is a word"); + uint32_t requiredVbSize = vertexCount * sizeof(ImDrawVert); + uint32_t requiredIbSize = indexCount * sizeof(uint16_t); + bool createVB = true; + bool createIB = true; + + if (mpVao) + { + createVB = mpVao->getVertexBuffer(0)->getSize() <= requiredVbSize; + createIB = mpVao->getIndexBuffer()->getSize() <= requiredIbSize; + + if (!createIB && !createVB) + { + return; + } + } + + // Need to create a new VAO + std::vector pVB(1); + pVB[0] = createVB ? Buffer::create(requiredVbSize + sizeof(ImDrawVert) * 1000, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr) : mpVao->getVertexBuffer(0); + Buffer::SharedPtr pIB = createIB ? Buffer::create(requiredIbSize, Buffer::BindFlags::Index, Buffer::CpuAccess::Write, nullptr) : mpVao->getIndexBuffer(); + mpVao = Vao::create(Vao::Topology::TriangleList, mpLayout, pVB, pIB, ResourceFormat::R16Uint); + } + + void GuiImpl::compileFonts() + { + uint8_t* pFontData; + int32_t width, height; + + // Initialize font data + ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pFontData, &width, &height); + Texture::SharedPtr pTexture = Texture::create2D(width, height, ResourceFormat::R8Unorm, 1, 1, pFontData); + mpProgramVars->setTexture("gFont", pTexture); + } + + bool GuiImpl::addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine) + { + bool modified = false; + std::string labelString(std::string("##") + label + '0'); + + for (uint32_t i = 0; i < numCheckboxes - 1; ++i) + { + labelString[labelString.size() - 1] = '0' + static_cast(i); + modified |= addCheckbox(labelString.c_str(), pData[i], (!i) ? sameLine : true); + } + + addCheckbox(label, pData[numCheckboxes - 1], true); + + return modified; + } + + void GuiImpl::setIoMouseEvents() + { + ImGuiIO& io = ImGui::GetIO(); + memcpy(io.MouseDown, mMouseEvents.buttonPressed, sizeof(mMouseEvents.buttonPressed)); + } + + void GuiImpl::resetMouseEvents() + { + for (uint32_t i = 0; i < arraysize(mMouseEvents.buttonPressed); i++) + { + if (mMouseEvents.buttonReleased[i]) + { + mMouseEvents.buttonPressed[i] = mMouseEvents.buttonReleased[i] = false; + } + } + } + + bool GuiImpl::beginMenu(const char* name) + { + return ImGui::BeginMenu(name); + } + + void GuiImpl::endMenu() + { + return ImGui::EndMenu(); + } + + bool GuiImpl::beginMainMenuBar() + { + bool isOpen = ImGui::BeginMainMenuBar(); + return isOpen; + } + + void GuiImpl::endMainMenuBar() + { + ImGui::EndMainMenuBar(); + } + + bool GuiImpl::beginDropDownMenu(const char label[]) + { + return ImGui::BeginMenu(label); + } + + void GuiImpl::endDropDownMenu() + { + ImGui::EndMenu(); + } + + bool GuiImpl::addMenuItem(const char label[], const char shortcut[]) + { + return ImGui::MenuItem(label, shortcut); + } + + bool GuiImpl::addMenuItem(const char label[], bool& var, const char shortcut[]) + { + return ImGui::MenuItem(label, shortcut, &var); + } + + bool GuiImpl::pushWindow(const char label[], bool& open, uvec2 size, uvec2 pos, Gui::WindowFlags flags) + { + bool allowClose = is_set(flags, Gui::WindowFlags::CloseButton); + if (allowClose) + { + if (!is_set(flags, Gui::WindowFlags::ShowTitleBar)) + { + std::string warning("Asking for a close button on window "); + logWarning(warning.append(label).append(", but the ShowTitleBar flag is not set on the window. The window will not be able to display a close button.")); + } + } + + vec2 posFloat(pos); + posFloat *= mScaleFactor; + ImVec2 fPos(posFloat.x, posFloat.y); + ImVec2 fSize(float(size.x), float(size.y)); + ImGui::SetNextWindowSize(fSize, ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(fPos, ImGuiCond_FirstUseEver); + int imguiFlags = 0; + if (!is_set(flags, Gui::WindowFlags::ShowTitleBar)) imguiFlags |= ImGuiWindowFlags_NoTitleBar; + if (!is_set(flags, Gui::WindowFlags::AllowMove)) imguiFlags |= ImGuiWindowFlags_NoMove; + if (!is_set(flags, Gui::WindowFlags::SetFocus)) imguiFlags |= ImGuiWindowFlags_NoFocusOnAppearing; + if (is_set(flags, Gui::WindowFlags::NoResize)) imguiFlags |= ImGuiWindowFlags_NoResize; + if (is_set(flags, Gui::WindowFlags::AutoResize)) imguiFlags |= ImGuiWindowFlags_AlwaysAutoResize; + + ImGui::Begin(label, allowClose ? &open : nullptr, imguiFlags); + + if (!open) ImGui::End(); + else ImGui::PushFont(mpActiveFont); + return open; + } + + void GuiImpl::popWindow() + { + ImGui::PopFont(); + ImGui::End(); + } + + void GuiImpl::setCurrentWindowPos(uint32_t x, uint32_t y) + { + ImGui::SetWindowPos({ static_cast(x), static_cast(y) }); + } + + void GuiImpl::setCurrentWindowSize(uint32_t width, uint32_t height) + { + ImGui::SetWindowSize({ static_cast(width), static_cast(height) }); + } + + void GuiImpl::beginColumns(uint32_t numColumns) + { + ImGui::Columns(numColumns); + } + + void GuiImpl::nextColumn() + { + ImGui::NextColumn(); + } + + bool GuiImpl::beginGroup(const char name[], bool beginExpanded) + { + std::string nameString(name); + ImGuiTreeNodeFlags flags = beginExpanded ? ImGuiTreeNodeFlags_DefaultOpen : 0; + bool visible = mGroupStackSize ? ImGui::TreeNodeEx(name, flags) : ImGui::CollapsingHeader(name, flags); + if (visible) mGroupStackSize++; + + std::string popupName = std::string("HeaderOptions##") + nameString; + if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(1)) ImGui::OpenPopup(popupName.c_str()); + + if (ImGui::BeginPopup(popupName.c_str())) + { + if (ImGui::Button("Open in Window")) ImGui::CloseCurrentPopup(); + if (ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } + ImGui::EndPopup(); + } + + return visible; + } + + void GuiImpl::endGroup() + { + assert(mGroupStackSize >= 1); + mGroupStackSize--; + if (mGroupStackSize) ImGui::TreePop(); + } + + void GuiImpl::indent(float i) + { + ImGui::Indent(i); + } + + void GuiImpl::addSeparator(uint32_t count) + { + for (uint32_t i = 0; i < count; i++) ImGui::Separator(); + } + + void GuiImpl::addDummyItem(const char label[], const glm::vec2& size, bool sameLine) + { + if (sameLine) ImGui::SameLine(); + ImGui::PushID(label); + ImGui::Dummy({ size.x, size.y }); + ImGui::PopID(); + } + + void GuiImpl::addRect(const glm::vec2& size, const glm::vec4& color, bool filled, bool sameLine) + { + if (sameLine) ImGui::SameLine(); + + const ImVec2& cursorPos = ImGui::GetCursorScreenPos(); + ImVec2 bottomLeft{ cursorPos.x + size.x, cursorPos.y + size.y }; + ImVec4 rectColor{ color.x, color.y, color.z, color.w }; + + if (filled) + { + ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); + } + else + { + ImGui::GetWindowDrawList()->AddRect(ImGui::GetCursorScreenPos(), bottomLeft, ImGui::ColorConvertFloat4ToU32(rectColor)); + } + } + + bool GuiImpl::addDropdown(const char label[], const Gui::DropdownList& values, uint32_t& var, bool sameLine) + { + if (sameLine) ImGui::SameLine(); + // Check if we need to update the currentItem + const auto& iter = mDropDownValues.find(label); + int curItem; + if ((iter == mDropDownValues.end()) || (iter->second.lastVal != var)) + { + // Search the current val + for (uint32_t i = 0; i < values.size(); i++) + { + if (values[i].value == var) + { + curItem = i; + mDropDownValues[label].currentItem = i; + break; + } + } + } + else + { + curItem = mDropDownValues[label].currentItem; + } + + std::string comboStr; + for (const auto& v : values) + { + comboStr += v.label + '\0'; + } + comboStr += '\0'; + uint32_t prevItem = curItem; + //This returns true if the combo is interacted with at all + bool b = ImGui::Combo(label, &curItem, comboStr.c_str()); + mDropDownValues[label].currentItem = curItem; + mDropDownValues[label].lastVal = values[curItem].value; + var = values[curItem].value; + //Only return true if value is changed + return b && prevItem != curItem; + } + + bool GuiImpl::addButton(const char label[], bool sameLine) + { + if (sameLine) ImGui::SameLine(); + return ImGui::Button(label); + } + + bool GuiImpl::addRadioButtons(const Gui::RadioButtonGroup& buttons, uint32_t& activeID) + { + int32_t oldValue = activeID; + + for (const auto& button : buttons) + { + if (button.sameLine) ImGui::SameLine(); + ImGui::RadioButton(button.label.c_str(), (int*)&activeID, button.buttonID); + } + + return oldValue != activeID; + } + + bool GuiImpl::addDirectionWidget(const char label[], glm::vec3& direction) + { + glm::vec3 dir = direction; + bool b = addVecVar(label, dir, -1.f, 1.f, 0.001f, false, "%.3f"); + direction = glm::normalize(dir); + return b; + } + + bool GuiImpl::addCheckbox(const char label[], bool& var, bool sameLine) + { + if (sameLine) ImGui::SameLine(); + return ImGui::Checkbox(label, &var); + } + + bool GuiImpl::addCheckbox(const char label[], int& var, bool sameLine) + { + bool value = (var != 0); + bool modified = addCheckbox(label, value, sameLine); + var = (value ? 1 : 0); + return modified; + } + + template + bool GuiImpl::addBoolVecVar(const char label[], T& var, bool sameLine) + { + return addCheckboxes(label, glm::value_ptr(var), var.length(), sameLine); + } + + bool GuiImpl::addDragDropSource(const char label[], const char dataLabel[], const std::string& payloadString) + { + if (ImGui::IsItemHovered() && (ImGui::IsMouseClicked(0) || ImGui::IsMouseClicked(1))) ImGui::SetWindowFocus(); + if (!(ImGui::IsWindowFocused())) return false; + bool b = ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID); + if (b) + { + ImGui::SetDragDropPayload(dataLabel, payloadString.data(), payloadString.size() * sizeof(payloadString[0]), ImGuiCond_Once); + ImGui::EndDragDropSource(); + } + return b; + } + + bool GuiImpl::addDragDropDest(const char dataLabel[], std::string& payloadString) + { + bool b = false; + if (ImGui::BeginDragDropTarget()) + { + auto dragDropPayload = ImGui::AcceptDragDropPayload(dataLabel); + b = dragDropPayload && dragDropPayload->IsDataType(dataLabel) && (dragDropPayload->Data != nullptr); + if (b) + { + payloadString.resize(dragDropPayload->DataSize); + std::memcpy(&payloadString.front(), dragDropPayload->Data, dragDropPayload->DataSize); + } + + ImGui::EndDragDropTarget(); + } + + return b; + } + + void GuiImpl::addText(const char text[], bool sameLine) + { + if (sameLine) ImGui::SameLine(); + ImGui::TextUnformatted(text); + } + + bool GuiImpl::addTextbox(const char label[], char buf[], size_t bufSize, uint32_t lineCount, Gui::TextFlags flags) + { + bool fitWindow = is_set(flags, Gui::TextFlags::FitWindow); + if (fitWindow) ImGui::PushItemWidth(ImGui::GetWindowWidth()); + + if (lineCount > 1) + { + const ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CtrlEnterForNewLine; + return ImGui::InputTextMultiline(label, buf, bufSize, ImVec2(-1.0f, ImGui::GetTextLineHeight() * lineCount), flags); + } + else + { + return ImGui::InputText(label, buf, bufSize, ImGuiInputTextFlags_EnterReturnsTrue); + } + + if (fitWindow) ImGui::PopItemWidth(); + } + + bool GuiImpl::addTextbox(const char label[], std::string& text, uint32_t lineCount, Gui::TextFlags flags) + { + static const int maxSize = 2048; + char buf[maxSize]; + copyStringToBuffer(buf, maxSize, text); + + bool result = addTextbox(label, buf, maxSize, lineCount, flags); + text = std::string(buf); + return result; + } + + bool GuiImpl::addMultiTextbox(const char label[], const std::vector& textLabels, std::vector& textEntries) + { + static uint32_t sIdOffset = 0; + bool result = false; + + for (uint32_t i = 0; i < textEntries.size(); ++i) + { + result |= addTextbox(std::string(textLabels[i] + "##" + std::to_string(sIdOffset)).c_str(), textEntries[i]); + } + + return addButton(label) | result; + } + + void GuiImpl::addTooltip(const char tip[], bool sameLine) + { + if (sameLine) ImGui::SameLine(); + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(450.0f); + ImGui::TextUnformatted(tip); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + } + + bool GuiImpl::addRgbColor(const char label[], glm::vec3& var, bool sameLine) + { + if (sameLine) ImGui::SameLine(); + return ImGui::ColorEdit3(label, glm::value_ptr(var)); + } + + bool GuiImpl::addRgbaColor(const char label[], glm::vec4& var, bool sameLine) + { + if (sameLine) ImGui::SameLine(); + return ImGui::ColorEdit4(label, glm::value_ptr(var)); + } + + void GuiImpl::addImage(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio, bool sameLine) + { + if (size == vec2(0)) + { + ImVec2 windowSize = ImGui::GetWindowSize(); + size = { windowSize.x, windowSize.y }; + } + + ImGui::PushID(label); + if (sameLine) ImGui::SameLine(); + mpImages.push_back(pTex); + float aspectRatio = maintainRatio ? (static_cast(pTex->getHeight()) / static_cast(pTex->getWidth())) : 1.0f; + ImGui::Image(reinterpret_cast(mpImages.size()), { size.x, maintainRatio ? size.x * aspectRatio : size.y }); + ImGui::PopID(); + } + + bool GuiImpl::addImageButton(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio, bool sameLine) + { + mpImages.push_back(pTex); + if (sameLine) ImGui::SameLine(); + float aspectRatio = maintainRatio ? (static_cast(pTex->getHeight()) / static_cast(pTex->getWidth())) : 1.0f; + return ImGui::ImageButton(reinterpret_cast(mpImages.size()), { size.x, maintainRatio ? size.x * aspectRatio : size.y }); + } + + template + bool addScalarVarHelper(const char label[], T& var, ImGuiDataType_ imguiType, T minVal, T maxVal, float step, bool sameLine, const char* displayFormat) + { + ImGui::PushItemWidth(200); + if (sameLine) ImGui::SameLine(); + bool b = ImGui::DragScalar(label, imguiType, &var, step, &minVal, &maxVal, displayFormat); + var = glm::clamp(var, T(minVal), T(maxVal)); + ImGui::PopItemWidth(); + return b; + } + + template + bool GuiImpl::addScalarVar(const char label[], T& var, T minVal, T maxVal, float step, bool sameLine, const char* displayFormat) + { + if (std::is_same::value) + { + return addScalarVarHelper(label, var, ImGuiDataType_S32, minVal, maxVal, step, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addScalarVarHelper(label, var, ImGuiDataType_U32, minVal, maxVal, step, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addScalarVarHelper(label, var, ImGuiDataType_Float, minVal, maxVal, step, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addScalarVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); + } + else + { + logError("Unsupported slider type"); + return false; + } + } + + template + bool addScalarSliderHelper(const char label[], T& var, ImGuiDataType_ imguiType, T minVal, T maxVal, bool sameLine, const char* displayFormat) + { + ImGui::PushItemWidth(200); + if (sameLine) ImGui::SameLine(); + bool b = ImGui::SliderScalar(label, imguiType, &var, &minVal, &maxVal, displayFormat); + ImGui::PopItemWidth(); + return b; + } + + template + bool GuiImpl::addScalarSlider(const char label[], T& var, T minVal, T maxVal, bool sameLine, const char* displayFormat) + { + if (std::is_same::value) + { + return addScalarSliderHelper(label, var, ImGuiDataType_S32, minVal, maxVal, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addScalarSliderHelper(label, var, ImGuiDataType_U32, minVal, maxVal, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addScalarSliderHelper(label, var, ImGuiDataType_Float, minVal, maxVal, sameLine, displayFormat); + } + else + { + logError("Unsupported slider type"); + return false; + } + } + + template + bool addVecVarHelper(const char label[], T& var, ImGuiDataType_ imguiType, typename T::value_type minVal, typename T::value_type maxVal, float step, bool sameLine, const char* displayFormat) + { + ImGui::PushItemWidth(200); + if (sameLine) ImGui::SameLine(); + bool b = ImGui::DragScalarN(label, imguiType, glm::value_ptr(var), var.length(), step, &minVal, &maxVal, displayFormat); + var = glm::clamp(var, T(minVal), T(maxVal)); + ImGui::PopItemWidth(); + return b; + } + + template + bool GuiImpl::addVecVar(const char label[], T& var, typename T::value_type minVal, typename T::value_type maxVal, float step, bool sameLine, const char* displayFormat) + { + if (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_S32, minVal, maxVal, step, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_U32, minVal, maxVal, step, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_Float, minVal, maxVal, step, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addVecVarHelper(label, var, ImGuiDataType_U64, minVal, maxVal, step, sameLine, displayFormat); + } + else + { + logError("Unsupported slider type"); + return false; + } + } + + template + bool addVecSliderHelper(const char label[], T& var, ImGuiDataType_ imguiType, typename T::value_type minVal, typename T::value_type maxVal, bool sameLine, const char* displayFormat) + { + ImGui::PushItemWidth(200); + if (sameLine) ImGui::SameLine(); + bool b = ImGui::SliderScalarN(label, imguiType, glm::value_ptr(var), var.length(), &minVal, &maxVal, displayFormat); + ImGui::PopItemWidth(); + return b; + } + + template + bool GuiImpl::addVecSlider(const char label[], T& var, typename T::value_type minVal, typename T::value_type maxVal, bool sameLine, const char* displayFormat) + { + if (std::is_same::value) + { + return addVecSliderHelper(label, var, ImGuiDataType_S32, minVal, maxVal, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addVecSliderHelper(label, var, ImGuiDataType_U32, minVal, maxVal, sameLine, displayFormat); + } + else if (std::is_same::value) + { + return addVecSliderHelper(label, var, ImGuiDataType_Float, minVal, maxVal, sameLine, displayFormat); + } + else + { + logError("Unsupported slider type"); + return false; + } + } + + template + bool GuiImpl::addMatrixVar(const char label[], MatrixType& var, float minVal, float maxVal, bool sameLine) + { + std::string labelString(label); + std::string hiddenLabelString("##"); + hiddenLabelString += labelString + "[0]"; + + ImVec2 topLeft = ImGui::GetCursorScreenPos(); + ImVec2 bottomRight; + + bool b = false; + + for (uint32_t i = 0; i < static_cast(var.length()); ++i) + { + std::string& stringToDisplay = hiddenLabelString; + hiddenLabelString[hiddenLabelString.size() - 2] = '0' + static_cast(i); + if (i == var.length() - 1) + { + stringToDisplay = labelString; + } + + b |= addVecVar(stringToDisplay.c_str(), var[i], minVal, maxVal, 0.001f, sameLine); + + if (i == 0) + { + ImGui::SameLine(); + bottomRight = ImGui::GetCursorScreenPos(); + float oldSpacing = ImGui::GetStyle().ItemSpacing.y; + ImGui::GetStyle().ItemSpacing.y = 0.0f; + ImGui::Dummy({}); + ImGui::Dummy({}); + ImGui::GetStyle().ItemSpacing.y = oldSpacing; + ImVec2 correctedCursorPos = ImGui::GetCursorScreenPos(); + correctedCursorPos.y += oldSpacing; + ImGui::SetCursorScreenPos(correctedCursorPos); + bottomRight.y = ImGui::GetCursorScreenPos().y; + } + else if (i == 1) + { + bottomRight.y = topLeft.y + (bottomRight.y - topLeft.y) * (var.length()); + bottomRight.x -= ImGui::GetStyle().ItemInnerSpacing.x * 3 - 1; + bottomRight.y -= ImGui::GetStyle().ItemInnerSpacing.y - 1; + topLeft.x -= 1; topLeft.y -= 1; + auto colorVec4 = ImGui::GetStyleColorVec4(ImGuiCol_ScrollbarGrab); colorVec4.w *= 0.25f; + ImU32 color = ImGui::ColorConvertFloat4ToU32(colorVec4); + ImGui::GetWindowDrawList()->AddRect(topLeft, bottomRight, color); + } + } + return b; + } + + void GuiImpl::addGraph(const char label[], Gui::GraphCallback func, void* pUserData, uint32_t sampleCount, int32_t sampleOffset, float yMin, float yMax, uint32_t width, uint32_t height) + { + ImVec2 imSize{ (float)width, (float)height }; + ImGui::PlotLines(label, func, pUserData, (int32_t)sampleCount, sampleOffset, nullptr, yMin, yMax, imSize); + } + + Gui::~Gui() + { + ImGui::DestroyContext(); + } + + Gui::UniquePtr Gui::create(uint32_t width, uint32_t height, float scaleFactor) + { + UniquePtr pGui = UniquePtr(new Gui); + pGui->mpWrapper = new GuiImpl(); + pGui->mpWrapper->init(pGui.get(), scaleFactor); + pGui->onWindowResize(width, height); + return pGui; + } + + glm::vec4 Gui::pickUniqueColor(const std::string& key) + { + union hashedValue + { + size_t st; + int32_t i32[2]; + }; + hashedValue color; + color.st = std::hash()(key); + + return glm::vec4(color.i32[0] % 1000 / 2000.0f, color.i32[1] % 1000 / 2000.0f, (color.i32[0] * color.i32[1]) % 1000 / 2000.0f, 1.0f); + } + + void Gui::addFont(const std::string& name, const std::string& filename) + { + std::string fullpath; + if (findFileInDataDirectories(filename, fullpath) == false) + { + logWarning("Can't find font file `" + filename + "`"); + return; + } + + float size = 14.0f * mpWrapper->mScaleFactor; + ImFont* pFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(fullpath.c_str(), size); + mpWrapper->mFontMap[name] = pFont; + mpWrapper->compileFonts(); + } + + void Gui::setActiveFont(const std::string& font) + { + const auto& it = mpWrapper->mFontMap.find(font); + if (it == mpWrapper->mFontMap.end()) + { + logWarning("Can't find a font named `" + font + "`"); + mpWrapper->mpActiveFont = nullptr; + } + mpWrapper->mpActiveFont = it->second; + } + + ImFont* Gui::getFont(std::string f) + { + if (f.size()) return mpWrapper->mFontMap.at(f); + else return mpWrapper->mpActiveFont; + } + + void Gui::beginFrame() + { + ImGui::NewFrame(); + } + + void Gui::setGlobalGuiScaling(float scale) + { + ImGuiIO& io = ImGui::GetIO(); + io.FontGlobalScale = scale; + ImGui::GetStyle().ScaleAllSizes(scale); + } + + void Gui::render(RenderContext* pContext, const Fbo::SharedPtr& pFbo, float elapsedTime) + { + while (mpWrapper->mGroupStackSize) mpWrapper->endGroup(); + + // Set the mouse state + mpWrapper->setIoMouseEvents(); + + ImGui::Render(); + ImDrawData* pDrawData = ImGui::GetDrawData(); + + mpWrapper->resetMouseEvents(); + + // Update the VAO + mpWrapper->createVao(pDrawData->TotalVtxCount, pDrawData->TotalIdxCount); + mpWrapper->mpPipelineState->setVao(mpWrapper->mpVao); + + // Upload the data + ImDrawVert* pVerts = (ImDrawVert*)mpWrapper->mpVao->getVertexBuffer(0)->map(Buffer::MapType::WriteDiscard); + uint16_t* pIndices = (uint16_t*)mpWrapper->mpVao->getIndexBuffer()->map(Buffer::MapType::WriteDiscard); + + for (int n = 0; n < pDrawData->CmdListsCount; n++) + { + const ImDrawList* pCmdList = pDrawData->CmdLists[n]; + memcpy(pVerts, pCmdList->VtxBuffer.Data, pCmdList->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(pIndices, pCmdList->IdxBuffer.Data, pCmdList->IdxBuffer.Size * sizeof(ImDrawIdx)); + pVerts += pCmdList->VtxBuffer.Size; + pIndices += pCmdList->IdxBuffer.Size; + } + mpWrapper->mpVao->getVertexBuffer(0)->unmap(); + mpWrapper->mpVao->getIndexBuffer()->unmap(); + mpWrapper->mpPipelineState->setFbo(pFbo); + + // Setup viewport + GraphicsState::Viewport vp; + vp.originX = 0; + vp.originY = 0; + vp.width = ImGui::GetIO().DisplaySize.x; + vp.height = ImGui::GetIO().DisplaySize.y; + vp.minDepth = 0; + vp.maxDepth = 1; + mpWrapper->mpPipelineState->setViewport(0, vp); + + // Render command lists + uint32_t vtxOffset = 0; + uint32_t idxOffset = 0; + + for (int n = 0; n < pDrawData->CmdListsCount; n++) + { + const ImDrawList* pCmdList = pDrawData->CmdLists[n]; + for (int32_t cmd = 0; cmd < pCmdList->CmdBuffer.Size; cmd++) + { + const ImDrawCmd* pCmd = &pCmdList->CmdBuffer[cmd]; + GraphicsState::Scissor scissor((int32_t)pCmd->ClipRect.x, (int32_t)pCmd->ClipRect.y, (int32_t)pCmd->ClipRect.z, (int32_t)pCmd->ClipRect.w); + if (pCmd->TextureId) + { + mpWrapper->mpProgramVars->getDefaultBlock()->setSrv(mpWrapper->mGuiImageLoc, 0, (mpWrapper->mpImages[reinterpret_cast(pCmd->TextureId) - 1])->getSRV()); + mpWrapper->mpProgramVars["PerFrameCB"]["useGuiImage"] = true; + } + else + { + mpWrapper->mpProgramVars["PerFrameCB"]["useGuiImage"] = false; + } + mpWrapper->mpPipelineState->setScissors(0, scissor); + pContext->drawIndexed(mpWrapper->mpPipelineState.get(), mpWrapper->mpProgramVars.get(), pCmd->ElemCount, idxOffset, vtxOffset); + idxOffset += pCmd->ElemCount; + } + vtxOffset += pCmdList->VtxBuffer.Size; + } + + // Prepare for the next frame + ImGuiIO& io = ImGui::GetIO(); + io.DeltaTime = elapsedTime; + mpWrapper->mGroupStackSize = 0; + + mpWrapper->mpImages.clear(); + } + + void Gui::onWindowResize(uint32_t width, uint32_t height) + { + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = (float)width; + io.DisplaySize.y = (float)height; +#ifdef FALCOR_VK + mpWrapper->mpProgramVars["PerFrameCB"]["scale"] = 2.0f / vec2(io.DisplaySize.x, io.DisplaySize.y); + mpWrapper->mpProgramVars["PerFrameCB"]["offset"] = vec2(-1.0f); +#else + mpWrapper->mpProgramVars["PerFrameCB"]["scale"] = 2.0f / vec2(io.DisplaySize.x, -io.DisplaySize.y); + mpWrapper->mpProgramVars["PerFrameCB"]["offset"] = vec2(-1.0f, 1.0f); +#endif + } + + bool Gui::onMouseEvent(const MouseEvent& event) + { + ImGuiIO& io = ImGui::GetIO(); + switch (event.type) + { + case MouseEvent::Type::LeftButtonDown: + mpWrapper->mMouseEvents.buttonPressed[0] = true; + break; + case MouseEvent::Type::LeftButtonUp: + mpWrapper->mMouseEvents.buttonReleased[0] = true; + break; + case MouseEvent::Type::RightButtonDown: + mpWrapper->mMouseEvents.buttonPressed[1] = true; + break; + case MouseEvent::Type::RightButtonUp: + mpWrapper->mMouseEvents.buttonReleased[1] = true; + break; + case MouseEvent::Type::MiddleButtonDown: + mpWrapper->mMouseEvents.buttonPressed[2] = true; + break; + case MouseEvent::Type::MiddleButtonUp: + mpWrapper->mMouseEvents.buttonReleased[2] = true; + break; + case MouseEvent::Type::Move: + io.MousePos.x = event.pos.x * io.DisplaySize.x; + io.MousePos.y = event.pos.y * io.DisplaySize.y; + break; + case MouseEvent::Type::Wheel: + io.MouseWheel += event.wheelDelta.y; + break; + } + + return io.WantCaptureMouse; + } + + bool Gui::onKeyboardEvent(const KeyboardEvent& event) + { + ImGuiIO& io = ImGui::GetIO(); + + if (event.type == KeyboardEvent::Type::Input) + { + std::string u8str = utf32ToUtf8(event.codepoint); + io.AddInputCharactersUTF8(u8str.c_str()); + + // Gui consumes keyboard input + return true; + } + else + { + uint32_t key = (uint32_t)(event.key == KeyboardEvent::Key::KeypadEnter ? KeyboardEvent::Key::Enter : event.key); + + switch (event.type) + { + case KeyboardEvent::Type::KeyPressed: + io.KeysDown[key] = true; + break; + case KeyboardEvent::Type::KeyReleased: + io.KeysDown[key] = false; + break; + default: + should_not_get_here(); + } + + io.KeyCtrl = event.mods.isCtrlDown; + io.KeyAlt = event.mods.isAltDown; + io.KeyShift = event.mods.isShiftDown; + io.KeySuper = false; + return io.WantCaptureKeyboard; + } + } + + void Gui::Widgets::indent(float i) + { + if (mpGui) mpGui->mpWrapper->indent(i); + } + + void Gui::Widgets::separator(uint32_t count) + { + if (mpGui) mpGui->mpWrapper->addSeparator(count); + } + + void Gui::Widgets::dummy(const char label[], const glm::vec2& size, bool sameLine) + { + if (mpGui) mpGui->mpWrapper->addDummyItem(label, size, sameLine); + } + + void Gui::Widgets::rect(const glm::vec2& size, const glm::vec4& color, bool filled, bool sameLine) + { + if (mpGui) mpGui->mpWrapper->addRect(size, color, filled, sameLine); + } + + bool Gui::Widgets::dropdown(const char label[], const DropdownList& values, uint32_t& var, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addDropdown(label, values, var, sameLine) : false; + } + + bool Gui::Widgets::button(const char label[], bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addButton(label, sameLine) : false; + } + + bool Gui::Widgets::radioButtons(const RadioButtonGroup& buttons, uint32_t& activeID) + { + return mpGui ? mpGui->mpWrapper->addRadioButtons(buttons, activeID) : false; + } + + bool Gui::Widgets::direction(const char label[], glm::vec3& direction) + { + return mpGui ? mpGui->mpWrapper->addDirectionWidget(label, direction) : false; + } + + template<> + dlldecl bool Gui::Widgets::checkbox(const char label[], bool& var, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addCheckbox(label, var, sameLine) : false; + } + + template<> + dlldecl bool Gui::Widgets::checkbox(const char label[], int& var, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addCheckbox(label, var, sameLine) : false; + } + + template + bool Gui::Widgets::checkbox(const char label[], T& var, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addBoolVecVar(label, var, sameLine) : false; + } + +#define add_bool_vec_type(TypeName) template dlldecl bool Gui::Widgets::checkbox(const char[], TypeName&, bool) + + add_bool_vec_type(glm::bvec2); + add_bool_vec_type(glm::bvec3); + add_bool_vec_type(glm::bvec4); + +#undef add_bool_vec_type + + bool Gui::Widgets::dragDropSource(const char label[], const char dataLabel[], const std::string& payloadString) + { + return mpGui ? mpGui->mpWrapper->addDragDropSource(label, dataLabel, payloadString) : false; + } + + bool Gui::Widgets::dragDropDest(const char dataLabel[], std::string& payloadString) + { + return mpGui ? mpGui->mpWrapper->addDragDropDest(dataLabel, payloadString) : false; + } + + template::value, bool>> + bool Gui::Widgets::var(const char label[], T& var, T minVal, T maxVal, float step, bool sameLine, const char* displayFormat) + { + return mpGui ? mpGui->mpWrapper->addScalarVar(label, var, minVal, maxVal, step, sameLine, displayFormat) : false; + } + +#define add_scalarVar_type(TypeName) template dlldecl bool Gui::Widgets::var(const char[], TypeName&, TypeName, TypeName, float, bool, const char*) + + add_scalarVar_type(int32_t); + add_scalarVar_type(uint32_t); + add_scalarVar_type(uint64_t); + add_scalarVar_type(float); + +#undef add_scalarVar_type + + template::value, bool>> + bool Gui::Widgets::slider(const char label[], T& var, T minVal, T maxVal, bool sameLine, const char* displayFormat) + { + T lowerBound = glm::clamp(minVal, std::numeric_limits::min() / 2, std::numeric_limits::max() / 2); + T upperBound = glm::clamp(maxVal, std::numeric_limits::min() / 2, std::numeric_limits::max() / 2); + return mpGui ? mpGui->mpWrapper->addScalarSlider(label, var, lowerBound, upperBound, sameLine, displayFormat) : false; + } + +#define add_scalarSlider_type(TypeName) template dlldecl bool Gui::Widgets::slider(const char[], TypeName&, TypeName, TypeName, bool, const char*) + + add_scalarSlider_type(int32_t); + add_scalarSlider_type(uint32_t); + add_scalarSlider_type(uint64_t); + add_scalarSlider_type(float); + +#undef add_scalarSlider_type + + template::value, bool>> + bool Gui::Widgets::var(const char label[], T& var, typename T::value_type minVal, typename T::value_type maxVal, float step, bool sameLine, const char* displayFormat) + { + return mpGui ? mpGui->mpWrapper->addVecVar(label, var, minVal, maxVal, step, sameLine, displayFormat) : false; + } + +#define add_vecVar_type(TypeName) template dlldecl bool Gui::Widgets::var(const char[], TypeName&, typename TypeName::value_type, typename TypeName::value_type, float, bool, const char*) + + add_vecVar_type(ivec2); + add_vecVar_type(ivec3); + add_vecVar_type(ivec4); + add_vecVar_type(uvec2); + add_vecVar_type(uvec3); + add_vecVar_type(uvec4); + add_vecVar_type(vec2); + add_vecVar_type(vec3); + add_vecVar_type(vec4); + +#undef add_vecVar_type + + template::value, bool>> + bool Gui::Widgets::slider(const char label[], T& var, typename T::value_type minVal, typename T::value_type maxVal, bool sameLine, const char* displayFormat) + { + typename T::value_type lowerBound = glm::clamp(minVal, std::numeric_limits::min() / 2, std::numeric_limits::max() / 2); + typename T::value_type upperBound = glm::clamp(maxVal, std::numeric_limits::min() / 2, std::numeric_limits::max() / 2); + return mpGui ? mpGui->mpWrapper->addVecSlider(label, var, lowerBound, upperBound, sameLine, displayFormat) : false; + } + +#define add_vecSlider_type(TypeName) template dlldecl bool Gui::Widgets::slider(const char[], TypeName&, typename TypeName::value_type, typename TypeName::value_type, bool, const char*) + + add_vecSlider_type(ivec2); + add_vecSlider_type(ivec3); + add_vecSlider_type(ivec4); + add_vecSlider_type(uvec2); + add_vecSlider_type(uvec3); + add_vecSlider_type(uvec4); + add_vecSlider_type(vec2); + add_vecSlider_type(vec3); + add_vecSlider_type(vec4); + +#undef add_vecSlider_type + + void Gui::Widgets::text(const std::string& text, bool sameLine) + { + if (mpGui) mpGui->mpWrapper->addText(text.c_str(), sameLine); + } + + bool Gui::Widgets::textbox(const std::string& label, std::string& text, TextFlags flags) + { + return mpGui ? mpGui->mpWrapper->addTextbox(label.c_str(), text, 1, flags) : false; + } + + bool Gui::Widgets::textbox(const char label[], char buf[], size_t bufSize, uint32_t lineCount, TextFlags flags) + { + return mpGui ? mpGui->mpWrapper->addTextbox(label, buf, bufSize, lineCount, flags) : false; + } + + bool Gui::Widgets::multiTextbox(const char label[], const std::vector& textLabels, std::vector& textEntries) + { + return mpGui ? mpGui->mpWrapper->addMultiTextbox(label, textLabels, textEntries) : false; + } + + void Gui::Widgets::tooltip(const std::string& text, bool sameLine) + { + if (mpGui) mpGui->mpWrapper->addTooltip(text.c_str(), sameLine); + } + + bool Gui::Widgets::rgbColor(const char label[], glm::vec3& var, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addRgbColor(label, var, sameLine) : false; + } + + bool Gui::Widgets::rgbaColor(const char label[], glm::vec4& var, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addRgbaColor(label, var, sameLine) : false; + } + + bool Gui::Widgets::imageButton(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addImageButton(label, pTex, size, maintainRatio, sameLine) : false; + } + + void Gui::Widgets::image(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio, bool sameLine) + { + if (mpGui) mpGui->mpWrapper->addImage(label, pTex, size, maintainRatio, sameLine); + } + + template + bool Gui::Widgets::matrix(const char label[], MatrixType& var, float minVal, float maxVal, bool sameLine) + { + return mpGui ? mpGui->mpWrapper->addMatrixVar(label, var, minVal, maxVal, sameLine) : false; + } + +#define add_matrix_var(TypeName) template dlldecl bool Gui::Widgets::matrix(const char[], TypeName&, float, float, bool) + + add_matrix_var(glm::mat2x2); + add_matrix_var(glm::mat2x3); + add_matrix_var(glm::mat2x4); + add_matrix_var(glm::mat3x2); + add_matrix_var(glm::mat3x3); + add_matrix_var(glm::mat3x4); + add_matrix_var(glm::mat4x2); + add_matrix_var(glm::mat4x3); + add_matrix_var(glm::mat4x4); + +#undef add_matrix_var + + void Gui::Widgets::graph(const char label[], GraphCallback func, void* pUserData, uint32_t sampleCount, int32_t sampleOffset, float yMin, float yMax, uint32_t width, uint32_t height) + { + if (mpGui) mpGui->mpWrapper->addGraph(label, func, pUserData, sampleCount, sampleOffset, yMin, yMax, width, height); + } + + Gui::Menu::Menu(Gui* pGui, const char* name) + { + if (pGui && pGui->mpWrapper->beginMenu(name)) mpGui = pGui; + } + + Gui::Menu::~Menu() + { + release(); + } + + void Gui::Menu::release() + { + if (mpGui) mpGui->mpWrapper->endMenu(); + mpGui = nullptr; + } + + Gui::Menu::Dropdown Gui::Menu::dropdown(const std::string& label) + { + return Dropdown(mpGui, label.c_str()); + } + + bool Gui::Menu::item(const std::string& label) + { + return mpGui && mpGui->mpWrapper->addMenuItem(label.c_str()); + } + + Gui::Menu::Dropdown::Dropdown(Gui* pGui, const char label[]) + { + if (pGui && pGui->mpWrapper->beginDropDownMenu(label)) mpGui = pGui; + } + + Gui::Menu::Dropdown::~Dropdown() + { + if (mpGui) release(); + } + + void Gui::Menu::Dropdown::release() + { + if (mpGui) mpGui->mpWrapper->endDropDownMenu(); mpGui = nullptr; + } + bool Gui::Menu::Dropdown::item(const std::string& label, bool& var, const std::string& shortcut) + { + return mpGui && mpGui->mpWrapper->addMenuItem(label.c_str(), var, shortcut.size() ? shortcut.c_str() : nullptr); + } + + bool Gui::Menu::Dropdown::item(const std::string& label, const std::string& shortcut) + { + return mpGui && mpGui->mpWrapper->addMenuItem(label.c_str(), shortcut.size() ? shortcut.c_str() : nullptr); + } + + void Gui::Menu::Dropdown::separator() + { + if (mpGui) mpGui->mpWrapper->addSeparator(); + } + + Gui::Menu Gui::Menu::Dropdown::menu(const char* name) + { + return Menu(mpGui, name); + } + + Gui::Group::Group(Gui* pGui, const std::string& label, bool beginExpanded) + { + if (pGui && pGui->mpWrapper->beginGroup(label, beginExpanded)) mpGui = pGui; + } + + bool Gui::Group::open() + { + return mpGui != nullptr; + } + + Gui::Group::~Group() + { + release(); + } + + void Gui::Group::release() + { + if (mpGui) mpGui->mpWrapper->endGroup(); mpGui = nullptr; + } + + Gui::Window::Window(Gui* pGui, const char* name, uvec2 size, uvec2 pos, Gui::WindowFlags flags) + { + bool open = true; + if (pGui->mpWrapper->pushWindow(name, open, size, pos, flags)) mpGui = pGui; + } + + Gui::Window::Window(Gui* pGui, const char* name, bool& open, uvec2 size, uvec2 pos, Gui::WindowFlags flags) + { + if (pGui->mpWrapper->pushWindow(name, open, size, pos, flags)) mpGui = pGui; + } + + Gui::Window::~Window() + { + release(); + } + + void Gui::Window::release() + { + if (mpGui) mpGui->mpWrapper->popWindow(); + mpGui = nullptr; + } + + Gui::Group Gui::Window::group(const std::string& label, bool beginExpanded) + { + return Group(mpGui, label, beginExpanded); + } + + void Gui::Window::columns(uint32_t numColumns) + { + if (mpGui) mpGui->mpWrapper->beginColumns(numColumns); + } + + void Gui::Window::nextColumn() + { + if (mpGui) mpGui->mpWrapper->nextColumn(); + } + + void Gui::Window::windowPos(uint32_t x, uint32_t y) + { + if (mpGui) mpGui->mpWrapper->setCurrentWindowPos(x, y); + } + + void Gui::Window::windowSize(uint32_t width, uint32_t height) + { + if (mpGui) mpGui->mpWrapper->setCurrentWindowSize(width, height); + } + + Gui::MainMenu::MainMenu(Gui* pGui) : Gui::Menu() + { + if (pGui->mpWrapper->beginMainMenuBar()) mpGui = pGui; + } + + Gui::MainMenu::~MainMenu() + { + release(); + } + + void Gui::MainMenu::release() + { + if (mpGui) + { + mpGui->mpWrapper->endMainMenuBar(); + mpGui = nullptr; + } + } +} diff --git a/Source/Falcor/Utils/UI/Gui.h b/Source/Falcor/Utils/UI/Gui.h new file mode 100644 index 000000000..15e4fe78d --- /dev/null +++ b/Source/Falcor/Utils/UI/Gui.h @@ -0,0 +1,530 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include +#include "Core/Program/ProgramVars.h" +#include "Core/API/VAO.h" +#include "Core/Program/GraphicsProgram.h" +#include "Core/State/GraphicsState.h" + +struct ImFont; + +namespace Falcor +{ + struct MouseEvent; + struct KeyboardEvent; + + class GuiImpl; + + // Helper to check if a class is a vector + template + struct is_vector : std::false_type {}; + + template + struct is_vector> : std::true_type {}; + + /** A class wrapping the external GUI library + */ + class dlldecl Gui + { + public: + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + using GraphCallback = float(*)(void*, int32_t index); + + /** These structs used to initialize dropdowns + */ + struct DropdownValue + { + uint32_t value; ///< User defined index. Should be unique between different options. + std::string label; ///< Label of the dropdown option. + }; + + using DropdownList = std::vector ; + + struct RadioButton + { + uint32_t buttonID; ///< User defined index. Should be unique between different options in the same group. + std::string label; ///< Label of the radio button. + bool sameLine; ///< Whether the button should appear on the same line as the previous widget/button. + }; + + using RadioButtonGroup = std::vector; + + enum class TextFlags + { + Empty = 0x0, + FitWindow = 0x1, // Also hides the label + }; + + enum class WindowFlags + { + Empty = 0x0, ///< No flags + ShowTitleBar = 0x1, ///< Show a title bar + AllowMove = 0x2, ///< Allow the window move + SetFocus = 0x4, ///< Take focus when the window appears + CloseButton = 0x8, ///< Add a close button + NoResize = 0x10, ///< Disable manual resizing + AutoResize = 0x20, ///< Auto resize the window to fit it's content every frame + + Default = ShowTitleBar | AllowMove | SetFocus | CloseButton + }; + + enum class WidgetFlags + { + Empty = 0x0, ///< No flags + SameLine = 0x1, ///< Show a title bar + Inactive = 0x2, ///< Inactive widget, disallow edits + }; + + class dlldecl Widgets + { + public: + /** Indent the next item + */ + void indent(float i); + + /** Add a separator + */ + void separator(uint32_t count = 1); + + /** Dummy object especially useful for spacing + \param[in] label. Name for id of item + \param[in] size. size in pixels of the item. + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + */ + void dummy(const char label[], const glm::vec2& size, bool sameLine = false); + + /** Display rectangle with specified color + \param[in] size size in pixels of rectangle + \param[in] color Optional. color as an rgba vec4 + \param[in] filled Optional. If set to true, rectangle will be filled + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + */ + void rect(const glm::vec2& size, const glm::vec4& color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), bool filled = false, bool sameLine = false); + + /** Adds a dropdown menu. This will update a user variable directly, so the user has to keep track of that for changes. + If you want notifications whenever the select option changed, use Gui#addDropdownWithCallback(). + \param[in] label The name of the dropdown menu. + \param[in] values A list of options to show in the dropdown menu. + \param[in] var A reference to a user variable that will be updated directly when a dropdown option changes. This correlates to the 'pValue' field in Gui#SDropdownValue struct. + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + \return true if the value changed, otherwise false + */ + bool dropdown(const char label[], const DropdownList& values, uint32_t& var, bool sameLine = false); + + /** Button. Will return true if the button was pressed + \param[in] label Text to display on the button + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + */ + bool button(const char label[], bool sameLine = false); + + /** Adds a group of radio buttons. + \param[in] buttons List of buttons to show. + \param[out] activeID If a button was clicked, activeID will be set to the ID of the clicked button. + \return Whether activeID changed. + */ + bool radioButtons(const RadioButtonGroup& buttons, uint32_t& activeID); + + /** Adds a direction widget + \param[in] label The name of the widget. + \param[in] direction A reference for the direction variable + \return true if the value changed, otherwise false + */ + bool direction(const char label[], glm::vec3& direction); + + /** Adds a UI widget for multiple checkboxes. + \param[in] label The name of the widget. + \param[in] var A reference to the bools that will be updated directly when the checkbox state changes (0 = unchecked, 1 = checked). + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + \return true if the value changed, otherwise false + */ + template + bool checkbox(const char label[], T& var, bool sameLine = false); + + /** The source for drag and drop. Call this to allow users to drag out of last gui item. + \param[in] label The name of the drag and drop widget + \param[in] dataLabel Destination that has same dataLabel can accept the payload + \param[in] payloadString Data in payload to be sent and accepted by destination. + \return true if user is clicking and dragging + */ + bool dragDropSource(const char label[], const char dataLabel[], const std::string& payloadString); + + /** Destination area for dropping data in drag and drop of last gui item. + \param[in] dataLabel Named label needs to be the same as source datalabel to accept payload. + \param[in] payloadString Data sent from the drag and drop source + \return true if payload is dropped. + */ + bool dragDropDest(const char dataLabel[], std::string& payloadString); + + // Text + /** Static text + \param[in] text The string to display + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + */ + void text(const std::string& text, bool sameLine = false); + + /** Adds a text box. + \param[in] label The name of the variable. + \param[in] text A string with the initialize text. The string will be updated if a text is entered. + \param[in] lineCount Number of lines in the text-box. If larger then 1 will create a multi-line box + \return true if the value changed, otherwise false + */ + bool textbox(const std::string& label, std::string& text, TextFlags flags = TextFlags::Empty); + + /** Adds a text box. + \param[in] label The name of the variable. + \param[in] buf A character buffer with the initialize text. The buffer will be updated if a text is entered. + \param[in] bufSize The size of the text buffer + \param[in] lineCount Number of lines in the text-box. If larger then 1 will create a multi-line box + \return true if the value changed, otherwise false + */ + bool textbox(const char label[], char buf[], size_t bufSize, uint32_t lineCount = 1, TextFlags flags = TextFlags::Empty); + + /** Adds multiple text boxes for one confirmation button + */ + bool multiTextbox(const char label[], const std::vector& textLabels, std::vector& textEntries); + + /** Render a tooltip. This will display a small question mark next to the last label item rendered and will display the tooltip if the user hover over it + \param[in] tip The tooltip's text + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + */ + void tooltip(const std::string& text, bool sameLine = true); + + // Colors + /** Adds an RGB color UI widget. + \param[in] label The name of the widget. + \param[in] var A reference to a vector that will be updated directly when the widget state changes. + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + \return true if the value changed, otherwise false + */ + bool rgbColor(const char label[], glm::vec3& var, bool sameLine = false); + + /** Adds an RGBA color UI widget. + \param[in] label The name of the widget. + \param[in] var A reference to a vector that will be updated directly when the widget state changes. + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + \return true if the value changed, otherwise false + */ + bool rgbaColor(const char label[], glm::vec4& var, bool sameLine = false); + + // Images + /** Display image within imgui + \param[in] label. Name for id for item. + \param[in] pTex. Pointer to texture resource to draw in imgui + \param[in] size. Size in pixels of the image to draw. 0 means fit to window + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + */ + void image(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size = vec2(0), bool maintainRatio = true, bool sameLine = false); + bool imageButton(const char label[], const Texture::SharedPtr& pTex, glm::vec2 size, bool maintainRatio = true, bool sameLine = false); + + // Scalars + /** Adds a UI element for setting scalar values. + \param[in] label The name of the widget. + \param[in] var A reference that will be updated directly when the widget state changes. + \param[in] minVal Optional. The minimum allowed value for the float. + \param[in] maxVal Optional. The maximum allowed value for the float. + \param[in] step Optional. The step rate for the float. (Only used for non-sliders) + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget. + \param[in] displayFormat Optional. Formatting string. + \return true if the value changed, otherwise false + */ + template::value, bool> = true> + bool var(const char label[], T& var, T minVal = std::numeric_limits::min(), T maxVal = std::numeric_limits::max(), float step = 1.0f, bool sameLine = false, const char* displayFormat = nullptr); + template::value, bool> = true> + bool slider(const char label[], T& var, T minVal = std::numeric_limits::min() / 2, T maxVal = std::numeric_limits::max() / 2, bool sameLine = false, const char* displayFormat = nullptr); + + // Vectors + /** Adds a UI element for setting vector values. + \param[in] label The name of the widget. + \param[in] var A reference that will be updated directly when the widget state changes. + \param[in] minVal Optional. The minimum allowed value for the float. + \param[in] maxVal Optional. The maximum allowed value for the float. + \param[in] step Optional. The step rate for the float. (Only used for non-sliders) + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget. + \param[in] displayFormat Optional. Formatting string. + \return true if the value changed, otherwise false + */ + template::value, bool> = true> + bool var(const char label[], T& var, typename T::value_type minVal = std::numeric_limits::min(), typename T::value_type maxVal = std::numeric_limits::max(), float step = 1.0f, bool sameLine = false, const char* displayFormat = nullptr); + + template::value, bool> = true> + bool slider(const char label[], T& var, typename T::value_type minVal = std::numeric_limits::min() / 2, typename T::value_type maxVal = std::numeric_limits::max() / 2, bool sameLine = false, const char* displayFormat = nullptr); + + // Matrices + /** Adds an matrix UI widget. + \param[in] label The name of the widget. + \param[in] var A reference to the matrix struct that will be updated directly when the widget state changes. + \param[in] minVal Optional. The minimum allowed value for the variable. + \param[in] maxVal Optional. The maximum allowed value for the variable. + \param[in] sameLine Optional. If set to true, the widget will appear on the same line as the previous widget + \return true if the value changed, otherwise false + */ + template + bool matrix(const char label[], MatrixType& var, float minVal = -FLT_MAX, float maxVal = FLT_MAX, bool sameLine = false); + + // Graph + /** Adds a graph based on a function + \param[in] label The name of the widget. + \param[in] func A function pointer to calculate the values in the graph + \param[in] pUserData A user-data pointer to pass to the callback function + \param[in] sampleCount Number of sample-points in the graph + \param[in] sampleOffset Optional. Determines the value for the center of the x-axis + \param[in] yMin Optional. The minimum value of the y-axis. Use FLT_MAX to auto-detect the range based on the function and the provided x-range + \param[in] yMax Optional. The maximum value of the y-axis. Use FLT_MAX to auto-detect the range based on the function and the provided x-range + \param[in] width Optional. The width of the graph widget. 0 means auto-detect (fits the widget to the GUI width) + \param[in] height Optional. The height of the graph widget. 0 means auto-detect (no idea what's the logic. Too short.) + */ + void graph(const char label[], GraphCallback func, void* pUserData, uint32_t sampleCount, int32_t sampleOffset, float yMin = FLT_MAX, float yMax = FLT_MAX, uint32_t width = 0, uint32_t height = 100); + + Gui* gui() const { return mpGui; } + + protected: + Widgets() = default; + Gui* mpGui = nullptr; + }; + + class dlldecl Menu + { + public: + /** Create a new menu + \param[in] pGui a pointer to the current Gui object + \param[in] name the name of the menu + */ + Menu(Gui* pGui, const char* name); + + ~Menu(); + + /** End the menu of items. + */ + void release(); + + class dlldecl Dropdown + { + public: + /** Create a new dropdown menu + \param[in] pGui a pointer to the current Gui object + \param[in] label the name of the menu + */ + Dropdown(Gui* pGui, const char label[]); + + ~Dropdown(); + + /** End the drop down menu list of items. + */ + void release(); + + /** Item to be displayed in dropdown menu for the main menu bar + \param[in] label name of item to list in the menu. + \param[in] var if the label is selected or not. + \param[in] shortcut Shortcut key. It's just for display purposes, it doesn't handle the keystroke + \return true if the option was selected in the dropdown + */ + bool item(const std::string& label, bool& var, const std::string& shortcut = ""); + + /** Item to be displayed in dropdown menu for the main menu bar + \param[in] label Name of item to list in the menu. + \param[in] shortcut Shortcut key. It's just for display purposes, it doesn't handle the keystroke + \return true if the option was selected in the dropdown + */ + bool item(const std::string& label, const std::string& shortcut = ""); + + /** Add a separator between menu items. + */ + void separator(); + + /** Adds a sub-menu within the current dropdown menu. + \param[in] name the name of the menu + */ + Menu menu(const char* name); + + Gui* mpGui = nullptr; + }; + + /** Begins a collapsible menu in the menu bar of menu items + \param[in] label name of drop down menu to be displayed. + */ + Dropdown dropdown(const std::string& label); + + /** Add an item to the menu. + \param[in] label name of the item to list in the menu. + */ + bool item(const std::string& label); + + protected: + Menu() = default; + Gui* mpGui = nullptr; + }; + + class dlldecl Group : public Widgets + { + public: + /** Create a collapsible group block + \param[in] a pointer to the current pGui object + \param[in] label Display name of the group + \param[in] beginExpanded Whether group should be expanded initially + */ + Group(Gui* pGui, const std::string& label, bool beginExpanded = false); + + /** Create a collapsible group block + \param[in] w a reference to a Widgets object + \param[in] label Display name of the group + \param[in] beginExpanded Whether group should be expanded initially + */ + Group(const Widgets& w, const std::string& label, bool beginExpanded = false) : Group(w.gui(), label, beginExpanded) {} + + /** Check if the current group is open or closed. + */ + bool open(); + + ~Group(); + + /** End a collapsible group block + */ + void release(); + }; + + class dlldecl Window : public Widgets + { + public: + /** Create a new window + \param[in] pGui a pointer to the current Gui object + \param[in] size size in pixels of the window + \param[in] pos position in pixels of the window + \param[in] flags Window flags to apply + */ + Window(Gui* pGui, const char* name, uvec2 size = { 0, 0 }, uvec2 pos = { 0, 0 }, Gui::WindowFlags flags = Gui::WindowFlags::Default); + Window(Gui* pGui, const char* name, bool& open, uvec2 size = { 0, 0 }, uvec2 pos = { 0, 0 }, Gui::WindowFlags flags = Gui::WindowFlags::Default); + + /** Create a new window + \param[in] w a reference to a Widgets object + \param[in] size size in pixels of the window + \param[in] pos position in pixels of the window + \param[in] flags Window flags to apply + */ + Window(const Widgets& w, const char* name, uvec2 size = { 0, 0 }, uvec2 pos = { 0, 0 }, Gui::WindowFlags flags = Gui::WindowFlags::Default) : Window(w.gui(), name, size, pos, flags) {} + Window(const Widgets& w, const char* name, bool& open, uvec2 size = { 0, 0 }, uvec2 pos = { 0, 0 }, Gui::WindowFlags flags = Gui::WindowFlags::Default) : Window(w.gui(), name, open, size, pos, flags) {} + + ~Window(); + + /** End the window. + */ + void release(); + + /** Begin a group within the current window + \param[in] label the name of the group + \param[in] beginExpanded Optional whether the group is open or closed by default + */ + Group group(const std::string& label, bool beginExpanded = false); + + /** Begin a column within the current window + \param[in] numColumns requires number of columns within the window. + */ + void columns(uint32_t numColumns); + + /** Proceed to the next column within the window. + */ + void nextColumn(); + + /** Set the current window position in pixels + \param[in] x horizontal window position in pixels + \param[in] y vertical window position in pixels + */ + void windowPos(uint32_t x, uint32_t y); + + /** Set the size of the current window in pixels + \param[in] width Window width in pixels + \param[in] height Window height in pixels + */ + void windowSize(uint32_t width, uint32_t height); + }; + + class dlldecl MainMenu : public Menu + { + public: + /** Create a new main menu bar. + \param[in] pGui a pointer to the current Gui object + */ + MainMenu(Gui* pGui); + + ~MainMenu(); + + /** End the main menu bar. + */ + void release(); + }; + + /** Create a new GUI object. Each object is essentially a container for a GUI window + */ + static UniquePtr create(uint32_t width, uint32_t height, float scaleFactor = 1.0f); + + ~Gui(); + + static glm::vec4 pickUniqueColor(const std::string& key); + + /** Add a font + */ + void addFont(const std::string& name, const std::string& filename); + + /** Set the active font + */ + void setActiveFont(const std::string& font); + + ImFont* getFont(std::string f = ""); + + /** Start a new frame. Must be called at the start of each frame + */ + void beginFrame(); + + /** Set global font size scaling + */ + static void setGlobalGuiScaling(float scale); + + /** Render the GUI + */ + void render(RenderContext* pContext, const Fbo::SharedPtr& pFbo, float elapsedTime); + + /** Handle window resize events + */ + void onWindowResize(uint32_t width, uint32_t height); + + /** Handle mouse events + */ + bool onMouseEvent(const MouseEvent& event); + + /** Handle keyboard events + */ + bool onKeyboardEvent(const KeyboardEvent& event); + private: + Gui() = default; + GuiImpl* mpWrapper = nullptr; + }; + + enum_class_operators(Gui::WindowFlags); + enum_class_operators(Gui::TextFlags); +} diff --git a/Framework/Source/Utils/Picking/Picking.cpp b/Source/Falcor/Utils/UI/Picking.cpp similarity index 86% rename from Framework/Source/Utils/Picking/Picking.cpp rename to Source/Falcor/Utils/UI/Picking.cpp index 67718ff5b..4a05ece08 100644 --- a/Framework/Source/Utils/Picking/Picking.cpp +++ b/Source/Falcor/Utils/UI/Picking.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,13 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -#include "Framework.h" -#include "Utils/Picking/Picking.h" -#include "Graphics/FboHelper.h" +#include "stdafx.h" +#include "Picking.h" +#include "Core/API/RenderContext.h" namespace Falcor { +#if 0 Picking::UniquePtr Picking::create(const Scene::SharedPtr& pScene, uint32_t fboWidth, uint32_t fboHeight) { return UniquePtr(new Picking(pScene, fboWidth, fboHeight)); @@ -45,12 +45,12 @@ namespace Falcor return mPickResult.pModelInstance != nullptr; } - ObjectInstance::SharedConstPtr Picking::getPickedMeshInstance() const + ObjectInstance::SharedConstPtr Picking::getPickedMeshInstance() const { return mPickResult.pMeshInstance; } - ObjectInstance::SharedConstPtr Picking::getPickedModelInstance() const + ObjectInstance::SharedConstPtr Picking::getPickedModelInstance() const { return mPickResult.pModelInstance; } @@ -60,7 +60,7 @@ namespace Falcor Fbo::Desc fboDesc; fboDesc.setColorTarget(0, Falcor::ResourceFormat::R16Int).setDepthStencilTarget(Falcor::ResourceFormat::D24UnormS8); - mpFBO = FboHelper::create2D(width, height, fboDesc); + mpFBO = Fbo::create2D(width, height, fboDesc); } void Picking::registerGizmos(const Gizmo::Gizmos& gizmos) @@ -88,11 +88,11 @@ namespace Falcor // Depth State DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(false).setStencilTest(true).setStencilRef(1); + dsDesc.setDepthEnabled(false).setStencilEnabled(true).setStencilFunc(DepthStencilState::Face::FrontAndBack, ComparisonFunc::Always).setStencilRef(1); dsDesc.setStencilOp(DepthStencilState::Face::FrontAndBack, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Replace); mpSetStencilDS = DepthStencilState::create(dsDesc); - dsDesc.setDepthTest(true).setStencilTest(true).setStencilFunc(DepthStencilState::Face::FrontAndBack, DepthStencilState::Func::NotEqual); + dsDesc.setDepthEnabled(true).setDepthFunc(ComparisonFunc::Less).setStencilFunc(DepthStencilState::Face::FrontAndBack, ComparisonFunc::Always).setStencilFunc(DepthStencilState::Face::FrontAndBack, DepthStencilState::Func::NotEqual); dsDesc.setStencilOp(DepthStencilState::Face::FrontAndBack, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Keep, DepthStencilState::StencilOp::Keep); mpExcludeStencilDS = DepthStencilState::create(dsDesc); @@ -109,19 +109,8 @@ namespace Falcor const glm::vec4 clearColor(-1.0f); pContext->clearFbo(mpFBO.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - // Save state - auto pPrevGraphicsState = pContext->getGraphicsState(); - mpGraphicsState->setScissors(0, mScissor); - - // Render - pContext->setGraphicsState(mpGraphicsState); - pContext->setGraphicsVars(mpProgramVars); - - SceneRenderer::renderScene(pContext, pCamera); - - // Restore state - pContext->setGraphicsState(pPrevGraphicsState); + SceneRenderer::renderScene(pContext, mpGraphicsState.get(), mpProgramVars.get(), pCamera); } void Picking::readPickResults(RenderContext* pContext) @@ -174,14 +163,14 @@ namespace Falcor } mpGraphicsState->setProgram(mpProgram); - currentData.pContext->setGraphicsVars(mpProgramVars); - + // TODO: Why did we change the program and vars here? +// currentData.pContext->setGraphicsVars(mpProgramVars); return SceneRenderer::setPerModelData(currentData); } bool Picking::setPerMeshInstanceData(const CurrentWorkingData& currentData, const Scene::ModelInstance* pModelInstance, const Model::MeshInstance* pMeshInstance, uint32_t drawInstanceID) { - ConstantBuffer* pCB = currentData.pContext->getGraphicsVars()->getConstantBuffer(kPerMeshCbName).get(); + ConstantBuffer* pCB = currentData.pVars->getConstantBuffer(kPerMeshCbName).get(); pCB->setBlob(¤tData.drawID, sDrawIDOffset + drawInstanceID * sizeof(uint32_t), sizeof(uint32_t)); mDrawIDToInstance[currentData.drawID] = Instance(pModelInstance->shared_from_this(), pMeshInstance->shared_from_this()); @@ -203,4 +192,5 @@ namespace Falcor mScissor.right = mScissor.left + 1; mScissor.bottom = mScissor.top + 1; } +#endif } diff --git a/Framework/Source/Utils/Picking/Picking.h b/Source/Falcor/Utils/UI/Picking.h similarity index 95% rename from Framework/Source/Utils/Picking/Picking.h rename to Source/Falcor/Utils/UI/Picking.h index 85d367e5b..6646167ec 100644 --- a/Framework/Source/Utils/Picking/Picking.h +++ b/Source/Falcor/Utils/UI/Picking.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,17 +26,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once +#include "Gizmo.h" +#include "Core/State/GraphicsState.h" +#include "Core/Program/ProgramVars.h" -#include "Graphics/Scene/SceneRenderer.h" -#include "Graphics/Model/ObjectInstance.h" -#include "Graphics/Scene/Editor/Gizmo.h" -#include - +#if 0 namespace Falcor { /** SceneRenderer extended to add picking capabilities. Determines which object in the scene was clicked by the mouse. */ - class Picking : public SceneRenderer + class dlldecl Picking : public SceneRenderer { public: using UniquePtr = std::unique_ptr; @@ -109,16 +108,15 @@ namespace Falcor Fbo::SharedPtr mpFBO; GraphicsState::SharedPtr mpGraphicsState; - GraphicsProgram::SharedPtr mpProgram; GraphicsVars::SharedPtr mpProgramVars; // Separate program for rotation gizmos because away-facing parts are discarded :( GraphicsProgram::SharedPtr mpRotGizmoProgram; - DepthStencilState::SharedPtr mpSetStencilDS; DepthStencilState::SharedPtr mpExcludeStencilDS; GraphicsState::Scissor mScissor; }; } +#endif diff --git a/Framework/Source/Utils/PixelZoom.cpp b/Source/Falcor/Utils/UI/PixelZoom.cpp similarity index 90% rename from Framework/Source/Utils/PixelZoom.cpp rename to Source/Falcor/Utils/UI/PixelZoom.cpp index 7a1f3e00a..eb131d0c7 100644 --- a/Framework/Source/Utils/PixelZoom.cpp +++ b/Source/Falcor/Utils/UI/PixelZoom.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,8 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Utils/PixelZoom.h" -#include "Graphics/FboHelper.h" +#include "stdafx.h" +#include "PixelZoom.h" +#include "Core/API/RenderContext.h" +#include "Utils/UI/UserInput.h" namespace Falcor { @@ -66,10 +68,10 @@ namespace Falcor void PixelZoom::onResizeSwapChain(const Fbo* pBackbuffer) { const Fbo::Desc& desc = pBackbuffer->getDesc(); - mpSrcBlitFbo = FboHelper::create2D(pBackbuffer->getWidth(), pBackbuffer->getHeight(), desc); + mpSrcBlitFbo = Fbo::create2D(pBackbuffer->getWidth(), pBackbuffer->getHeight(), desc); if(mpDstBlitFbo == nullptr) { - mpDstBlitFbo = FboHelper::create2D(mDstZoomSize, mDstZoomSize, desc); + mpDstBlitFbo = Fbo::create2D(mDstZoomSize, mDstZoomSize, desc); } } @@ -105,7 +107,7 @@ namespace Falcor //negative to swap scroll up to zoom in and scroll down to zoom out int32_t zoomDelta = -1 * mZoomCoefficient * (int32_t)me.wheelDelta.y; mSrcZoomSize = max(mSrcZoomSize + zoomDelta, 3); - return true; + return me.type != MouseEvent::Type::Move; // Do not inhibit other passes from receiving mouse movement events. } return false; } diff --git a/Framework/Source/Utils/PixelZoom.h b/Source/Falcor/Utils/UI/PixelZoom.h similarity index 94% rename from Framework/Source/Utils/PixelZoom.h rename to Source/Falcor/Utils/UI/PixelZoom.h index fc456e14c..40f6672db 100644 --- a/Framework/Source/Utils/PixelZoom.h +++ b/Source/Falcor/Utils/UI/PixelZoom.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,11 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Framework.h" -#include "API/RenderContext.h" +#include "Core/API/FBO.h" namespace Falcor { + class RenderContext; + struct MouseEvent; + struct KeyboardEvent; + /** Magnifies a region of the screen to assist with inspecting details */ class PixelZoom diff --git a/Source/Falcor/Utils/UI/TextRenderer.cpp b/Source/Falcor/Utils/UI/TextRenderer.cpp new file mode 100644 index 000000000..119d3ee99 --- /dev/null +++ b/Source/Falcor/Utils/UI/TextRenderer.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "TextRenderer.h" +#include "Core/API/RenderContext.h" + +namespace Falcor +{ + namespace + { + struct Vertex + { + glm::vec2 screenPos; + glm::vec2 texCoord; + }; + + const glm::vec2 kVertexPos[] = + { + glm::vec2(0, 0), + glm::vec2(0, 1), + glm::vec2(1, 0), + + glm::vec2(1, 0), + glm::vec2(0, 1), + glm::vec2(1, 1), + }; + + const uint32_t kMaxCharCount = 1000; + + Vao::SharedPtr createVAO(const Buffer::SharedPtr& pVB) + { + VertexLayout::SharedPtr pLayout = VertexLayout::create(); + VertexBufferLayout::SharedPtr pBufLayout = VertexBufferLayout::create(); + pBufLayout->addElement("POSITION", 0, ResourceFormat::RG32Float, 1, 0); + pBufLayout->addElement("TEXCOORD", 8, ResourceFormat::RG32Float, 1, 1); + pLayout->addBufferLayout(0, pBufLayout); + Vao::BufferVec buffers{ pVB }; + + return Vao::create(Vao::Topology::TriangleList, pLayout, buffers); + } + + struct TextData + { + bool init = false; + TextRenderer::Flags flags = TextRenderer::Flags::Shadowed; + vec3 color = vec3(1, 1, 1); + Buffer::SharedPtr pVb; + RasterPass::SharedPtr pPass; + Font::UniquePtr pFont; + } gTextData; + + void setCbData(const Fbo::SharedPtr& pDstFbo) + { + float width = (float)pDstFbo->getWidth(); + float height = (float)pDstFbo->getHeight(); + + // Set the matrix + glm::mat4 vpTransform; + vpTransform[0][0] = 2 / width; + vpTransform[1][1] = -2 / height; + vpTransform[3][0] = -1; + vpTransform[3][1] = 1; +#ifdef FALCOR_VK + vpTransform[1][1] *= -1.0f; + vpTransform[3][1] *= -1.0f; +#endif + + // Update the program variables + gTextData.pPass["PerFrameCB"]["gvpTransform"] = vpTransform; + gTextData.pPass["PerFrameCB"]["gFontColor"] = gTextData.color; + } + + void renderText(RenderContext* pRenderContext, const std::string& text, const Fbo::SharedPtr& pDstFbo, vec2 pos) + { + // Make sure we enough space for the next char + assert(text.size() < kMaxCharCount); + setCbData(pDstFbo); + Vertex* verts = (Vertex*)gTextData.pVb->map(Buffer::MapType::WriteDiscard); + + float startX = pos.x; + uint32_t vertexCount = 0; // Not the same as text.size(), since some special characters are ignored + + // Create the vertex-buffer + for (const auto& c : text) + { + if (c == '\n') + { + pos.y += gTextData.pFont->getFontHeight(); + pos.x = startX; + } + else if (c == '\t') pos.x += gTextData.pFont->getTabWidth(); + else if (c == ' ') pos.x += gTextData.pFont->getLettersSpacing(); + else + { + // Regular character + const Font::CharTexCrdDesc& desc = gTextData.pFont->getCharDesc(c); + for (uint32_t i = 0; i < arraysize(kVertexPos); i++, vertexCount++) + { + vec2 posScale = kVertexPos[i]; + vec2 charPos = desc.size * posScale; + charPos += pos; + verts[vertexCount].screenPos = charPos; + verts[vertexCount].texCoord = desc.topLeft + desc.size * kVertexPos[i]; + } + pos.x += gTextData.pFont->getLettersSpacing(); + } + } + + // Submit + gTextData.pVb->unmap(); + gTextData.pPass->getState()->setFbo(pDstFbo); + gTextData.pPass->draw(pRenderContext, vertexCount, 0); + } + } + + const vec3& TextRenderer::getColor() { return gTextData.color; } + void TextRenderer::setColor(const glm::vec3& color) { gTextData.color = color; } + TextRenderer::Flags TextRenderer::getFlags() { return gTextData.flags; } + void TextRenderer::setFlags(Flags f) { gTextData.flags = f; } + + void TextRenderer::start() + { + if (gTextData.init) return; + + static const std::string kShaderFile("Framework/Shaders/TextRenderer.slang"); + + // Create a vertex buffer + const uint32_t vbSize = (uint32_t)(sizeof(Vertex)*kMaxCharCount*arraysize(kVertexPos)); + gTextData.pVb = Buffer::create(vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr); + + // Create the RenderState + gTextData.pPass = RasterPass::create(kShaderFile, "vs", "ps"); + auto& pState = gTextData.pPass->getState(); + pState->setVao(createVAO(gTextData.pVb)); + + // create the depth-state + DepthStencilState::Desc dsDesc; + dsDesc.setDepthEnabled(false); + pState->setDepthStencilState(DepthStencilState::create(dsDesc)); + + // Rasterizer state + RasterizerState::Desc rsState; + rsState.setCullMode(RasterizerState::CullMode::None); + pState->setRasterizerState(RasterizerState::create(rsState)); + + // Blend state + BlendState::Desc blendDesc; + blendDesc.setRtBlend(0, true).setRtParams(0, BlendState::BlendOp::Add, + BlendState::BlendOp::Add, + BlendState::BlendFunc::SrcAlpha, + BlendState::BlendFunc::OneMinusSrcAlpha, + BlendState::BlendFunc::One, + BlendState::BlendFunc::One); + pState->setBlendState(BlendState::create(blendDesc)); + gTextData.pFont = Font::create(); + + // Initialize the buffer + gTextData.pPass["gFontTex"] = gTextData.pFont->getTexture(); + + gTextData.init = true; + } + + void TextRenderer::shutdown() + { + gTextData = {}; + } + + void TextRenderer::render(RenderContext* pRenderContext, const std::string& text, const Fbo::SharedPtr& pDstFbo, vec2 pos) + { + if (is_set(gTextData.flags, TextRenderer::Flags::Shadowed)) + { + vec3 oldColor = getColor(); + setColor(vec3(0)); + renderText(pRenderContext, text, pDstFbo, pos + vec2(1)); + setColor(oldColor); + } + renderText(pRenderContext, text, pDstFbo, pos); + } +} diff --git a/Framework/Source/Effects/NormalMap/LeanMap.h b/Source/Falcor/Utils/UI/TextRenderer.h similarity index 52% rename from Framework/Source/Effects/NormalMap/LeanMap.h rename to Source/Falcor/Utils/UI/TextRenderer.h index ec82b4970..c313a2050 100644 --- a/Framework/Source/Effects/NormalMap/LeanMap.h +++ b/Source/Falcor/Utils/UI/TextRenderer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,51 +26,60 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include -#include "API/Texture.h" -#include "API/Sampler.h" +#include "Core/API/FBO.h" namespace Falcor { - class Texture; - class Scene; - class Material; - class ProgramVars; + class RenderContext; - class LeanMap + /** Class that renders text into the screen. + */ + class dlldecl TextRenderer { public: - using UniquePtr = std::unique_ptr; + enum class Flags + { + None = 0x0, + Shadowed = 0x1 + }; - /** Create Lean maps from materials used in a scene + /** Initialize the text-renderer + This class is not thread-safe! */ - static UniquePtr create(const Falcor::Scene* pScene); + static void start(); - /** Create a Lean map from a normal map + /** End batching. This will cause the render queue to flush and display the message to the screen. */ - static Falcor::Texture::SharedPtr createFromNormalMap(const Falcor::Texture* pNormalMap); + static void shutdown(); - /** Get a generated Lean map. - \param[in] sceneMaterialID Material ID to get Lean map for. Use Material::getId. + /** Render text + \param[in] pRenderContext A render-context which will be used to dispatch the draw + \param[in] text The text to draw. It can include newlines, tabs, carriage returns and regular ASCII characters. + \param[in] pDstFbo The target FBO + \param[in] pos Text position */ - Falcor::Texture* getLeanMap(uint32_t sceneMaterialID) { return mpLeanMaps[sceneMaterialID].get(); } + static void render(RenderContext* pRenderContext, const std::string& text, const Fbo::SharedPtr& pDstFbo, vec2 pos); + + /** Returns the color of the text being rendered + \return current color The text color + */ + static const glm::vec3& getColor(); - /** Set Lean map texture into a program. - \param[in] pVars Program vars to set into - \param[in] pSampler Sampler to use when sampling the Lean map + /** Set the color of the text being rendered + \param[in] color The text color */ - void setIntoProgramVars(ProgramVars* pVars, const Sampler::SharedPtr& pSampler) const; + static void setColor(const glm::vec3& color); - /** Get the array size required in the shader to hold Lean maps. Lean map index in shaders - match 1:1 with the material ID, but Lean maps for a contiguous range of materials may not have been generated. + /** Get the active flags */ - uint32_t getRequiredLeanMapShaderArraySize() const { return mShaderArraySize; } + static Flags getFlags(); + /** Set the flags + */ + static void setFlags(Flags f); private: - LeanMap() = default; - bool createLeanMap(const Falcor::Material* pMaterial); - std::map mpLeanMaps; - uint32_t mShaderArraySize = 0; + TextRenderer() = default; }; -} \ No newline at end of file + + enum_class_operators(TextRenderer::Flags); +} diff --git a/Framework/Source/Utils/UserInput.h b/Source/Falcor/Utils/UI/UserInput.h similarity index 97% rename from Framework/Source/Utils/UserInput.h rename to Source/Falcor/Utils/UI/UserInput.h index fa4dee135..2dc1d2ea3 100644 --- a/Framework/Source/Utils/UserInput.h +++ b/Source/Falcor/Utils/UI/UserInput.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,8 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec2.hpp" -#include namespace Falcor { @@ -60,6 +58,7 @@ namespace Falcor Type type; ///< Event Type. glm::vec2 pos; ///< Normalized coordinates x,y in range [0, 1]. (0,0) is the top-left corner of the window. + glm::vec2 screenPos; ///< Screen-space coordinates in range [0, clientSize]. (0,0) is the top-left corner of the window. glm::vec2 wheelDelta; ///< If the current event is CMouseEvent#Type#Wheel, the change in wheel scroll. Otherwise zero. InputModifiers mods; ///< Keyboard modifiers. Only valid if the event Type is one the button events }; @@ -194,4 +193,4 @@ namespace Falcor InputModifiers mods; ///< Keyboard modifiers uint32_t codepoint = 0; ///< UTF-32 codepoint from GLFW for Input event types }; -} \ No newline at end of file +} diff --git a/Framework/Source/Utils/Video/VideoEncoder.cpp b/Source/Falcor/Utils/Video/VideoEncoder.cpp similarity index 59% rename from Framework/Source/Utils/Video/VideoEncoder.cpp rename to Source/Falcor/Utils/Video/VideoEncoder.cpp index 217903563..4f51d11f8 100644 --- a/Framework/Source/Utils/Video/VideoEncoder.cpp +++ b/Source/Falcor/Utils/Video/VideoEncoder.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,178 +25,178 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" +#include "stdafx.h" #include "VideoEncoder.h" -#include "Utils/BinaryFileStream.h" extern "C" { -#include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" } namespace Falcor { - AVPixelFormat getPictureFormatFromCodec(AVCodecID codec) + namespace { - switch(codec) + AVPixelFormat getPictureFormatFromCodec(AVCodecID codec) { - case AV_CODEC_ID_RAWVIDEO: - return AV_PIX_FMT_BGR24; - case AV_CODEC_ID_H264: - case AV_CODEC_ID_HEVC: - case AV_CODEC_ID_MPEG2VIDEO: - return AV_PIX_FMT_YUV422P; - case AV_CODEC_ID_MPEG4: - return AV_PIX_FMT_YUV420P; - default: - should_not_get_here(); - return AV_PIX_FMT_NONE; + switch (codec) + { + case AV_CODEC_ID_RAWVIDEO: + return AV_PIX_FMT_BGR24; + case AV_CODEC_ID_H264: + case AV_CODEC_ID_HEVC: + case AV_CODEC_ID_MPEG2VIDEO: + return AV_PIX_FMT_YUV422P; + case AV_CODEC_ID_MPEG4: + return AV_PIX_FMT_YUV420P; + default: + should_not_get_here(); + return AV_PIX_FMT_NONE; + } } - } - AVPixelFormat getPictureFormatFromFalcorFormat(ResourceFormat format) - { - switch(format) + AVPixelFormat getPictureFormatFromFalcorFormat(ResourceFormat format) { - case ResourceFormat::RGBA8Unorm: - case ResourceFormat::RGBA8UnormSrgb: - return AV_PIX_FMT_RGBA; - case ResourceFormat::BGRA8Unorm: - case ResourceFormat::BGRA8UnormSrgb: - return AV_PIX_FMT_BGRA; - default: - should_not_get_here(); - return AV_PIX_FMT_NONE; + switch (format) + { + case ResourceFormat::RGBA8Unorm: + case ResourceFormat::RGBA8UnormSrgb: + return AV_PIX_FMT_RGBA; + case ResourceFormat::BGRA8Unorm: + case ResourceFormat::BGRA8UnormSrgb: + return AV_PIX_FMT_BGRA; + default: + return AV_PIX_FMT_NONE; + } } - } - AVCodecID getCodecID(VideoEncoder::CodecID codec) - { - switch(codec) + AVCodecID getCodecID(VideoEncoder::Codec codec) { - case VideoEncoder::CodecID::RawVideo: - return AV_CODEC_ID_RAWVIDEO; - case VideoEncoder::CodecID::H264: - return AV_CODEC_ID_H264; - case VideoEncoder::CodecID::HEVC: - return AV_CODEC_ID_HEVC; - case VideoEncoder::CodecID::MPEG2: - return AV_CODEC_ID_MPEG2VIDEO; - case VideoEncoder::CodecID::MPEG4: - return AV_CODEC_ID_MPEG4; - default: - should_not_get_here(); - return AV_CODEC_ID_NONE; + switch (codec) + { + case VideoEncoder::Codec::Raw: + return AV_CODEC_ID_RAWVIDEO; + case VideoEncoder::Codec::H264: + return AV_CODEC_ID_H264; + case VideoEncoder::Codec::HEVC: + return AV_CODEC_ID_HEVC; + case VideoEncoder::Codec::MPEG2: + return AV_CODEC_ID_MPEG2VIDEO; + case VideoEncoder::Codec::MPEG4: + return AV_CODEC_ID_MPEG4; + default: + should_not_get_here(); + return AV_CODEC_ID_NONE; + } } - } - static bool error(const std::string& filename, const std::string& msg) - { - std::string s("Error when creating video capture file "); - s += filename + ".\n" + msg; - logError(msg); - return false; - } - - AVCodecContext* createCodecContext(AVFormatContext* pCtx, uint32_t width, uint32_t height, uint32_t fps, float bitrateMbps, uint32_t gopSize, AVCodecID codecID, AVCodec* pCodec) - { - // Initialize the codec context - AVCodecContext* pCodecCtx = avcodec_alloc_context3(pCodec); - pCodecCtx->codec_id = codecID; - pCodecCtx->bit_rate = (int)(bitrateMbps * 1000 * 1000); - pCodecCtx->width = width; - pCodecCtx->height = height; - pCodecCtx->time_base = {1, (int)fps}; - pCodecCtx->gop_size = gopSize; - pCodecCtx->pix_fmt = getPictureFormatFromCodec(codecID); - - // Some formats want stream headers to be separate - if(pCtx->oformat->flags & AVFMT_GLOBALHEADER) + static bool error(const std::string& filename, const std::string& msg) { - pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + std::string s("Error when creating video capture file "); + s += filename + ".\n" + msg; + logError(msg); + return false; } - return pCodecCtx; - } - - AVStream* createVideoStream(AVFormatContext* pCtx, uint32_t fps, AVCodecID codecID, const std::string& filename, AVCodec*& pCodec) - { - // Get the encoder - pCodec = avcodec_find_encoder(codecID); - if(pCodec == nullptr) + AVCodecContext* createCodecContext(AVFormatContext* pCtx, uint32_t width, uint32_t height, uint32_t fps, float bitrateMbps, uint32_t gopSize, AVCodecID codecID, AVCodec* pCodec) { - error(filename, std::string("Can't find ") + avcodec_get_name(codecID) + " encoder."); - return nullptr; - } + // Initialize the codec context + AVCodecContext* pCodecCtx = avcodec_alloc_context3(pCodec); + pCodecCtx->codec_id = codecID; + pCodecCtx->bit_rate = (int)(bitrateMbps * 1000 * 1000); + pCodecCtx->width = width; + pCodecCtx->height = height; + pCodecCtx->time_base = { 1, (int)fps }; + pCodecCtx->gop_size = gopSize; + pCodecCtx->pix_fmt = getPictureFormatFromCodec(codecID); + + // Some formats want stream headers to be separate + if (pCtx->oformat->flags & AVFMT_GLOBALHEADER) + { + pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + } - // create the video stream - AVStream* pStream = avformat_new_stream(pCtx, nullptr); - if(pStream == nullptr) - { - error(filename, "Failed to create video stream."); - return nullptr; + return pCodecCtx; } - pStream->id = pCtx->nb_streams - 1; - pStream->time_base = {1, (int)fps}; - return pStream; - } - AVFrame* allocateFrame(int format, uint32_t width, uint32_t height, const std::string& filename) - { - AVFrame* pFrame = av_frame_alloc(); - if(pFrame == nullptr) + AVStream* createVideoStream(AVFormatContext* pCtx, uint32_t fps, AVCodecID codecID, const std::string& filename, AVCodec*& pCodec) { - error(filename, "Video frame allocation failed."); - return nullptr; - } + // Get the encoder + pCodec = avcodec_find_encoder(codecID); + if (pCodec == nullptr) + { + error(filename, std::string("Can't find ") + avcodec_get_name(codecID) + " encoder."); + return nullptr; + } - pFrame->format = format; - pFrame->width = width; - pFrame->height = height; - pFrame->pts = 0; + // create the video stream + AVStream* pStream = avformat_new_stream(pCtx, nullptr); + if (pStream == nullptr) + { + error(filename, "Failed to create video stream."); + return nullptr; + } + pStream->id = pCtx->nb_streams - 1; + pStream->time_base = { 1, (int)fps }; + return pStream; + } - // Allocate the buffer for the encoded image - if(av_frame_get_buffer(pFrame, 32) < 0) + AVFrame* allocateFrame(int format, uint32_t width, uint32_t height, const std::string& filename) { - error(filename, "Can't allocate destination picture"); - return nullptr; - } - - return pFrame; - } + AVFrame* pFrame = av_frame_alloc(); + if (pFrame == nullptr) + { + error(filename, "Video frame allocation failed."); + return nullptr; + } - bool openVideo(AVCodec* pCodec, AVCodecContext* pCodecCtx, AVFrame*& pFrame, const std::string& filename) - { - AVDictionary* param = nullptr; + pFrame->format = format; + pFrame->width = width; + pFrame->height = height; + pFrame->pts = 0; - if(pCodecCtx->codec_id == AV_CODEC_ID_H264) - { - // H.264 defaults to lossless currently. This should be changed in the future. - av_dict_set(¶m, "qp", "0", 0); - /* - Change options to trade off compression efficiency against encoding speed. If you specify a preset, the changes it makes will be applied before all other parameters are applied. - You should generally set this option to the slowest you can bear. - Values available: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo. - */ - av_dict_set(¶m, "preset", "veryslow", 0); - } + // Allocate the buffer for the encoded image + if (av_frame_get_buffer(pFrame, 32) < 0) + { + error(filename, "Can't allocate destination picture"); + return nullptr; + } - // Open the codec - if(avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) - { - return error(filename, "Can't open video codec."); + return pFrame; } - av_dict_free(¶m); - // create a frame - pFrame = allocateFrame(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, filename); - if(pFrame == nullptr) + bool openVideo(AVCodec* pCodec, AVCodecContext* pCodecCtx, AVFrame*& pFrame, const std::string& filename) { - return false; + AVDictionary* param = nullptr; + + if (pCodecCtx->codec_id == AV_CODEC_ID_H264) + { + // H.264 defaults to lossless currently. This should be changed in the future. + av_dict_set(¶m, "qp", "0", 0); + /* + Change options to trade off compression efficiency against encoding speed. If you specify a preset, the changes it makes will be applied before all other parameters are applied. + You should generally set this option to the slowest you can bear. + Values available: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo. + */ + av_dict_set(¶m, "preset", "veryslow", 0); + } + + // Open the codec + if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) + { + return error(filename, "Can't open video codec."); + } + av_dict_free(¶m); + + // create a frame + pFrame = allocateFrame(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, filename); + if (pFrame == nullptr) + { + return false; + } + return true; } - return true; } VideoEncoder::VideoEncoder(const std::string& filename) : mFilename(filename) @@ -224,6 +224,11 @@ namespace Falcor return pVC; } + bool VideoEncoder::isFormatSupported(ResourceFormat format) + { + return getPictureFormatFromFalcorFormat(format) != AV_PIX_FMT_NONE; + } + bool VideoEncoder::init(const Desc& desc) { // av_register_all() is deprecated since 58.9.100, but Linux repos may not get a newer version, so this call cannot be completely removed. @@ -290,6 +295,7 @@ namespace Falcor mpFlippedImage = new uint8_t[desc.height * mRowPitch]; } + assert(isFormatSupported(desc.format)); mpSwsContext = sws_getContext(desc.width, desc.height, getPictureFormatFromFalcorFormat(desc.format), desc.width, desc.height, mpCodecContext->pix_fmt, SWS_POINT, nullptr, nullptr, nullptr); if(mpSwsContext == nullptr) { @@ -392,7 +398,7 @@ namespace Falcor } } - FileDialogFilterVec VideoEncoder::getSupportedContainerForCodec(CodecID codec) + FileDialogFilterVec VideoEncoder::getSupportedContainerForCodec(Codec codec) { FileDialogFilterVec filters; const FileDialogFilter AVI{ "avi", "AVI (Audio Video Interleaved)"}; @@ -401,17 +407,17 @@ namespace Falcor switch(codec) { - case VideoEncoder::CodecID::RawVideo: + case VideoEncoder::Codec::Raw: filters.push_back(AVI); break; - case VideoEncoder::CodecID::H264: - case VideoEncoder::CodecID::MPEG2: - case VideoEncoder::CodecID::MPEG4: + case VideoEncoder::Codec::H264: + case VideoEncoder::Codec::MPEG2: + case VideoEncoder::Codec::MPEG4: filters.push_back(MP4); filters.push_back(MKV); filters.push_back(AVI); break; - case VideoEncoder::CodecID::HEVC: + case VideoEncoder::Codec::HEVC: filters.push_back(MP4); filters.push_back(MKV); break; @@ -420,4 +426,10 @@ namespace Falcor } return filters; } -} \ No newline at end of file + + SCRIPT_BINDING(VideoEncoder) + { + auto codec = m.enum_("Codec").regEnumVal(VideoEncoder::Codec::Raw).regEnumVal(VideoEncoder::Codec::MPEG4).regEnumVal(VideoEncoder::Codec::MPEG2); + codec.regEnumVal(VideoEncoder::Codec::H264).regEnumVal(VideoEncoder::Codec::HEVC); + } +} diff --git a/Framework/Source/Utils/Video/VideoEncoder.h b/Source/Falcor/Utils/Video/VideoEncoder.h similarity index 83% rename from Framework/Source/Utils/Video/VideoEncoder.h rename to Source/Falcor/Utils/Video/VideoEncoder.h index 9a921e893..d5f33e521 100644 --- a/Framework/Source/Utils/Video/VideoEncoder.h +++ b/Source/Falcor/Utils/Video/VideoEncoder.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include struct AVFormatContext; struct AVStream; @@ -36,15 +35,15 @@ struct AVCodecContext; namespace Falcor { - class VideoEncoder + class dlldecl VideoEncoder { public: using UniquePtr = std::unique_ptr; using UniqueConstPtr = std::unique_ptr; - enum class CodecID : int32_t + enum class Codec : int32_t { - RawVideo, + Raw, H264, HEVC, MPEG2, @@ -58,7 +57,7 @@ namespace Falcor uint32_t height = 0; float bitrateMbps = 4; uint32_t gopSize = 10; - CodecID codec = CodecID::RawVideo; + Codec codec = Codec::Raw; ResourceFormat format = ResourceFormat::BGRA8UnormSrgb; bool flipY = false; std::string filename; @@ -66,11 +65,12 @@ namespace Falcor ~VideoEncoder(); + static bool isFormatSupported(ResourceFormat format); static UniquePtr create(const Desc& desc); void appendFrame(const void* pData); void endCapture(); - static FileDialogFilterVec getSupportedContainerForCodec(CodecID codec); + static FileDialogFilterVec getSupportedContainerForCodec(Codec codec); private: VideoEncoder(const std::string& filename); bool init(const Desc& desc); @@ -86,4 +86,21 @@ namespace Falcor uint32_t mRowPitch = 0; uint8_t* mpFlippedImage = nullptr; // Used in case the image memory layout if bottom->top }; -} \ No newline at end of file + + inline std::string to_string(VideoEncoder::Codec c) + { +#define c2s(c_) case VideoEncoder::Codec::c_: return #c_ + switch (c) + { + c2s(Raw); + c2s(H264); + c2s(HEVC); + c2s(MPEG2); + c2s(MPEG4); + default: + should_not_get_here(); + return ""; + } +#undef c2s + } +} diff --git a/Source/Falcor/Utils/Video/VideoEncoderUI.cpp b/Source/Falcor/Utils/Video/VideoEncoderUI.cpp new file mode 100644 index 000000000..5cceea73f --- /dev/null +++ b/Source/Falcor/Utils/Video/VideoEncoderUI.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "VideoEncoderUI.h" +#include "Utils/UI/Gui.h" + +namespace Falcor +{ + static const Gui::DropdownList kCodecID = + { + { (uint32_t)VideoEncoder::Codec::Raw, std::string("Uncompressed") }, + { (uint32_t)VideoEncoder::Codec::H264, std::string("H.264") }, + { (uint32_t)VideoEncoder::Codec::HEVC, std::string("HEVC(H.265)") }, + { (uint32_t)VideoEncoder::Codec::MPEG2, std::string("MPEG2") }, + { (uint32_t)VideoEncoder::Codec::MPEG4, std::string("MPEG4") } + }; + + VideoEncoderUI::UniquePtr VideoEncoderUI::create(Callback startCaptureCB, Callback endCaptureCB) + { + return UniquePtr(new VideoEncoderUI(startCaptureCB, endCaptureCB)); + } + + VideoEncoderUI::VideoEncoderUI(Callback startCaptureCB, Callback endCaptureCB) : mStartCB(startCaptureCB), mEndCB(endCaptureCB) {} + + void VideoEncoderUI::render(Gui::Window& w, bool codecOnly) + { + mCapturing ? endCaptureUI(w, codecOnly) : startCaptureUI(w, codecOnly); + } + + void VideoEncoderUI::setCaptureState(bool state) + { + mCapturing = state; + } + + void VideoEncoderUI::startCaptureUI(Gui::Window& w, bool codecOnly) + { + { + auto g = w.group("Codec Options"); + g.dropdown("Codec", kCodecID, (uint32_t&)mCodec); + g.var("Video FPS", mFPS, 0u, 240u, 1); + g.var("Bitrate (Mbps)", mBitrate, 0.f, FLT_MAX, 0.01f); + g.var("GOP Size", mGopSize, 0u, 100000u, 1); + } + + if (codecOnly) return; + + w.checkbox("Capture UI", mCaptureUI); + w.tooltip("Check this box if you want the GUI recorded"); + w.checkbox("Reset rendering", mResetOnFirstFrame); + w.tooltip("Check this box if you want the rendering to be reset for the first frame, for example to reset temporal accumulation"); + w.checkbox("Use Time-Range", mUseTimeRange); + if(mUseTimeRange) + { + auto g = w.group("Time Range"); + g.var("Start Time", mStartTime, 0.f, FLT_MAX, 0.001f); + g.var("End Time", mEndTime, 0.f, FLT_MAX, 0.001f); + } + + if (mStartCB && w.button("Start Recording")) startCapture(); + if (mEndCB && w.button("Cancel", true)) mEndCB(); + } + + VideoEncoderUI::~VideoEncoderUI() = default; + + void VideoEncoderUI::startCapture() + { + if(saveFileDialog(VideoEncoder::getSupportedContainerForCodec(mCodec), mFilename)) + { + mCapturing = true; + mStartCB(); + } + } + + void VideoEncoderUI::endCaptureUI(Gui::Window& w, bool codecOnly) + { + if(mEndCB) + { + if (w.button("End Recording")) + { + mEndCB(); + mCapturing = false; + } + } + } +} diff --git a/Framework/Source/Utils/Video/VideoEncoderUI.h b/Source/Falcor/Utils/Video/VideoEncoderUI.h similarity index 73% rename from Framework/Source/Utils/Video/VideoEncoderUI.h rename to Source/Falcor/Utils/Video/VideoEncoderUI.h index 38421f0d3..2b09205a5 100644 --- a/Framework/Source/Utils/Video/VideoEncoderUI.h +++ b/Source/Falcor/Utils/Video/VideoEncoderUI.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,56 +33,57 @@ namespace Falcor { class Gui; - class VideoEncoderUI + class dlldecl VideoEncoderUI { public: using UniquePtr = std::unique_ptr; using UniqueConstPtr = std::unique_ptr; using Callback = std::function; - static UniquePtr create(uint32_t topLeftX, uint32_t topLeftY, uint32_t width, uint32_t height, Callback startCaptureCB, Callback endCaptureCB); + static UniquePtr create(Callback startCaptureCB, Callback endCaptureCB); ~VideoEncoderUI(); - void render(Gui* pGui); + void render(Gui::Window& w, bool codecOnly = false); void setCaptureState(bool state); - VideoEncoder::CodecID getCodec() const { return mCodec; } + VideoEncoder::Codec getCodec() const { return mCodec; } uint32_t getFPS() const { return mFPS; } + float getBitrate() const { return mBitrate; } + uint32_t getGopSize() const { return mGopSize; } + + VideoEncoderUI& setCodec(VideoEncoder::Codec c) { mCodec = c; return *this; } + VideoEncoderUI& setFPS(uint32_t fps) { mFPS = fps; return *this; } + VideoEncoderUI& setBitrate(float bitrate) { mBitrate = bitrate; return *this; } + VideoEncoderUI& setGopSize(uint32_t gopSize) { mGopSize = gopSize; return *this; } + bool useTimeRange() const { return mUseTimeRange; } bool captureUI() const { return mCaptureUI; } - bool resetOnFirstFrame() const { return mResetOnFirstFrame; } float getStartTime() const { return mStartTime; } float getEndTime() const { return mEndTime; } const std::string& getFilename() const { return mFilename; } - float getBitrate() const {return mBitrate; } - uint32_t getGopSize() const {return mGopSize; } private: - VideoEncoderUI(uint32_t topLeftX, uint32_t topLeftY, uint32_t width, uint32_t height, Callback startCaptureCB, Callback endCaptureCB); + VideoEncoderUI(Callback startCaptureCB, Callback endCaptureCB); void startCapture(); - void startCaptureUI(Gui* pGui); - void endCaptureUI(Gui* pGui); + void startCaptureUI(Gui::Window& w, bool codecOnly); + void endCaptureUI(Gui::Window& w, bool codecOnly); bool mCapturing = false; Callback mStartCB = nullptr; Callback mEndCB = nullptr; uint32_t mFPS = 60; - VideoEncoder::CodecID mCodec = VideoEncoder::CodecID::RawVideo; + VideoEncoder::Codec mCodec = VideoEncoder::Codec::Raw; bool mUseTimeRange = false; bool mCaptureUI = false; bool mResetOnFirstFrame = false; float mStartTime = 0; float mEndTime = FLT_MAX; - struct - { - uint32_t x, y, width, height; - } mWindowDims; std::string mFilename; float mBitrate = 4; uint32_t mGopSize = 10; }; -} \ No newline at end of file +} diff --git a/Source/Falcor/dependencies.xml b/Source/Falcor/dependencies.xml new file mode 100644 index 000000000..2e08322bf --- /dev/null +++ b/Source/Falcor/dependencies.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Falcor/stdafx.cpp b/Source/Falcor/stdafx.cpp new file mode 100644 index 000000000..b2b676591 --- /dev/null +++ b/Source/Falcor/stdafx.cpp @@ -0,0 +1,28 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" diff --git a/Source/Falcor/stdafx.h b/Source/Falcor/stdafx.h new file mode 100644 index 000000000..4c7962a08 --- /dev/null +++ b/Source/Falcor/stdafx.h @@ -0,0 +1,29 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" diff --git a/Source/Mogwai/Data/BSDFViewer.py b/Source/Mogwai/Data/BSDFViewer.py new file mode 100644 index 000000000..b06ff948a --- /dev/null +++ b/Source/Mogwai/Data/BSDFViewer.py @@ -0,0 +1,15 @@ +def render_graph_DefaultRenderGraph(): + g = RenderGraph("DefaultRenderGraph") + loadRenderPassLibrary("AccumulatePass.dll") + loadRenderPassLibrary("BSDFViewer.dll") + BSDFViewer = RenderPass("BSDFViewer") + g.addPass(BSDFViewer, "BSDFViewer") + AccumulatePass = RenderPass("AccumulatePass", {'enableAccumulation': True, 'precisionMode': AccumulatePrecision.Double}) + g.addPass(AccumulatePass, "AccumulatePass") + g.addEdge("BSDFViewer.output", "AccumulatePass.input") + g.markOutput("AccumulatePass.output") + return g + +DefaultRenderGraph = render_graph_DefaultRenderGraph() +try: m.addGraph(DefaultRenderGraph) +except NameError: None diff --git a/Source/Mogwai/Data/Config.py b/Source/Mogwai/Data/Config.py new file mode 100644 index 000000000..31ef54cbc --- /dev/null +++ b/Source/Mogwai/Data/Config.py @@ -0,0 +1,32 @@ +# Scene +m.loadScene("Arcade/Arcade.fscene") + +# Graphs +m.script("Data/ForwardRenderer.py") + +# Window Configuration +m.resizeSwapChain(1920, 1080) +m.ui(true) + +# Global Settings +t.now(0) +t.framerate(60) +# If framerate() is not zero, you can use the following function to set the start frame +# t.frame(0) + +t.exitTime(600) + +# Frame Capture +fc.outputDir("../../../Source/Mogwai") +fc.baseFilename("Mogwai") +g = m.activeGraph(); +#fc.frames(g, [20, 50, 32]) + +# Video Capture +vc.outputDir(".") +vc.baseFilename("Mogwai") +vc.codec(Codec.H264) +vc.fps(60) +vc.bitrate(4.000000) +vc.gopSize(10) +#vc.ranges(g, [[30, 300]]); diff --git a/Samples/RenderGraph/RenderGraphViewer/Data/forward_renderer.py b/Source/Mogwai/Data/ForwardRenderer.py similarity index 57% rename from Samples/RenderGraph/RenderGraphViewer/Data/forward_renderer.py rename to Source/Mogwai/Data/ForwardRenderer.py index 5dff8ec5f..ad22ebee0 100644 --- a/Samples/RenderGraph/RenderGraphViewer/Data/forward_renderer.py +++ b/Source/Mogwai/Data/ForwardRenderer.py @@ -1,14 +1,14 @@ def render_graph_forward_renderer(): - skyBox = createRenderPass("SkyBox") + skyBox = RenderPass("SkyBox") - forward_renderer = createRenderGraph("Forward Renderer") - forward_renderer.addPass(createRenderPass("DepthPass"), "DepthPrePass") - forward_renderer.addPass(createRenderPass("ForwardLightingPass"), "LightingPass") - forward_renderer.addPass(createRenderPass("CascadedShadowMaps"), "ShadowPass") - forward_renderer.addPass(createRenderPass("BlitPass"), "BlitPass") - forward_renderer.addPass(createRenderPass("ToneMapping"), "ToneMapping") - forward_renderer.addPass(createRenderPass("SSAO"), "SSAO") - forward_renderer.addPass(createRenderPass("FXAA"), "FXAA") + forward_renderer = RenderGraph("ForwardRenderer") + forward_renderer.addPass(RenderPass("DepthPass"), "DepthPrePass") + forward_renderer.addPass(RenderPass("ForwardLightingPass"), "LightingPass") + forward_renderer.addPass(RenderPass("CascadedShadowMaps"), "ShadowPass") + forward_renderer.addPass(RenderPass("BlitPass"), "BlitPass") + forward_renderer.addPass(RenderPass("ToneMappingPass"), "ToneMapping") + forward_renderer.addPass(RenderPass("SSAOPass"), "SSAO") + forward_renderer.addPass(RenderPass("FXAAPass"), "FXAA") forward_renderer.addPass(skyBox, "SkyBox") @@ -28,4 +28,6 @@ def render_graph_forward_renderer(): return forward_renderer -forward_renderer2 = render_graph_forward_renderer() \ No newline at end of file +forward_renderer = render_graph_forward_renderer() +try: m.addGraph(forward_renderer) +except NameError: None diff --git a/Source/Mogwai/Data/PathTracer.py b/Source/Mogwai/Data/PathTracer.py new file mode 100644 index 000000000..17329d7ac --- /dev/null +++ b/Source/Mogwai/Data/PathTracer.py @@ -0,0 +1,30 @@ +def render_graph_DefaultRenderGraph(): + g = RenderGraph("PathTracerGraph") + loadRenderPassLibrary("AccumulatePass.dll") + loadRenderPassLibrary("GBuffer.dll") + loadRenderPassLibrary("PathTracer.dll") + AccumulatePass = RenderPass("AccumulatePass", {'enableAccumulation': True}) + g.addPass(AccumulatePass, "AccumulatePass") + ToneMappingPass = RenderPass("ToneMappingPass", {'operator': ToneMapOp.Photo, 'exposureValue': 0.0, 'filmSpeed': 100.0, 'whitePoint': 6500.0, 'applyAcesCurve': 1}) + g.addPass(ToneMappingPass, "ToneMappingPass") + GBufferRT = RenderPass("GBufferRT", {'forceCullMode': False, 'cull': CullMode.CullBack, 'samplePattern': SamplePattern.Stratified, 'sampleCount': 16}) + g.addPass(GBufferRT, "GBufferRT") + MegakernelPathTracer = RenderPass("MegakernelPathTracer") + g.addPass(MegakernelPathTracer, "MegakernelPathTracer") + g.addEdge("GBufferRT.posW", "MegakernelPathTracer.posW") + g.addEdge("GBufferRT.normW", "MegakernelPathTracer.normalW") + g.addEdge("GBufferRT.bitangentW", "MegakernelPathTracer.bitangentW") + g.addEdge("GBufferRT.faceNormalW", "MegakernelPathTracer.faceNormalW") + g.addEdge("GBufferRT.viewW", "MegakernelPathTracer.viewW") + g.addEdge("GBufferRT.diffuseOpacity", "MegakernelPathTracer.mtlDiffOpacity") + g.addEdge("GBufferRT.specRough", "MegakernelPathTracer.mtlSpecRough") + g.addEdge("GBufferRT.emissive", "MegakernelPathTracer.mtlEmissive") + g.addEdge("GBufferRT.matlExtra", "MegakernelPathTracer.mtlParams") + g.addEdge("MegakernelPathTracer.color", "AccumulatePass.input") + g.addEdge("AccumulatePass.output", "ToneMappingPass.src") + g.markOutput("ToneMappingPass.dst") + return g + +DefaultRenderGraph = render_graph_DefaultRenderGraph() +try: m.addGraph(DefaultRenderGraph) +except NameError: None diff --git a/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp b/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp new file mode 100644 index 000000000..d13e5bb52 --- /dev/null +++ b/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "CaptureTrigger.h" +#include + +namespace Mogwai +{ + namespace + { + const std::string kReset = "reset"; + const std::string kRemoveGraph = "removeGraph"; + const std::string kOutputDir = "outputDir"; + const std::string kBaseFilename = "baseFilename"; + + void throwIfOverlapping(uint64_t xStart, uint64_t xCount, uint64_t yStart, uint64_t yCount) + { + uint64_t xEnd = xStart + xCount - 1; + uint64_t yEnd = yStart + yCount - 1; + if (xStart <= yEnd && yStart <= xEnd) + { + throw std::exception("This range overlaps an existing range!"); + } + } + + template + std::optional findRange(const T& frames, uint64_t startFrame) + { + for (auto r : frames) + { + if (r.first == startFrame) return r; + } + return std::nullopt; + } + } + + CaptureTrigger::CaptureTrigger(Renderer* pRenderer) : mpRenderer(pRenderer) {} + + void CaptureTrigger::addRange(const RenderGraph* pGraph, uint64_t startFrame, uint64_t count) + { + auto& ranges = mGraphRanges[pGraph]; + + if (count == 0) + { + for (auto r = ranges.begin(); r != ranges.end(); r++) + { + if (r->first == startFrame) + { + ranges.erase(r); + return; + } + } + } + + for (auto& range : ranges) + { + if (startFrame == range.first && count == range.second) continue; // Silently ignore existing ranges + throwIfOverlapping(startFrame, count, range.first, range.second); + } + + ranges.push_back({ startFrame, count }); + } + + void CaptureTrigger::reset(const RenderGraph* pGraph) + { + if (pGraph) mGraphRanges.erase(pGraph); + else mGraphRanges.clear(); + } + + void CaptureTrigger::beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + { + RenderGraph* pGraph = mpRenderer->getActiveGraph(); + if (!pGraph) return; + uint64_t frameId = gpFramework->getGlobalClock().frame(); + if (mGraphRanges.find(pGraph) == mGraphRanges.end()) return; + const auto& ranges = mGraphRanges.at(pGraph); + + if (mCurrent.pGraph) + { + assert(pGraph == mCurrent.pGraph); + return; + } + + // Check if we need to start a range + for (auto& r : ranges) + { + if (r.first == frameId) + { + mCurrent.pGraph = pGraph; + mCurrent.range = r; + beginRange(pGraph, r); + break; + } + } + } + + void CaptureTrigger::endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + { + if (!mCurrent.pGraph) return; + uint64_t frameId = gpFramework->getGlobalClock().frame(); + const auto& ranges = mGraphRanges.at(mCurrent.pGraph); + + triggerFrame(pRenderContext, mCurrent.pGraph, frameId); + + uint64_t end = mCurrent.range.first + mCurrent.range.second; + if (frameId + 1 == end) + { + endRange(mCurrent.pGraph, mCurrent.range); + mCurrent = {}; + } + } + + void CaptureTrigger::activeGraphChanged(RenderGraph* pNewGraph, RenderGraph* pPrevGraph) + { + if (mCurrent.pGraph) + { + endRange(mCurrent.pGraph, mCurrent.range); + mCurrent = {}; + } + } + + void CaptureTrigger::renderUI(Gui::Window& w) + { + w.textbox("Base Filename", mBaseFilename); + w.text("Output Directory\n" + mOutputDir); + std::string folder; + bool changed = w.button("Change Folder") && chooseFolderDialog(mOutputDir); + changed = w.checkbox("Absolute Path", mAbsolutePath, true) || changed; // Avoid short-circuit + if (changed) setOutputDirectory(mOutputDir); + w.tooltip("If checked, will use an absolute path. Otherwise, the path will be relative to the executable directory"); + } + + void CaptureTrigger::setOutputDirectory(const std::string& outDir) + { + bool absolute = std::filesystem::path(outDir).is_absolute(); + if (absolute && !mAbsolutePath) mOutputDir = std::filesystem::relative(outDir, getExecutableDirectory()).string(); + else if (!absolute && mAbsolutePath) mOutputDir = std::filesystem::absolute(getExecutableDirectory() + "/" + outDir).string(); + else mOutputDir = outDir; + } + + void CaptureTrigger::scriptBindings(Bindings& bindings) + { + auto& m = bindings.getModule(); + if (m.classExists()) return; + auto ct = m.class_("CaptureTrigger"); + + // Members + ct.func_(kReset.c_str(), &CaptureTrigger::reset, "renderGraph"_a = nullptr); + + // Output dir + ct.func_(kOutputDir.c_str(), &CaptureTrigger::setOutputDirectory); + auto printOutDir = [](CaptureTrigger* pCT) {pybind11::print(pCT->mOutputDir); }; + ct.func_(kOutputDir.c_str(), printOutDir); + + // Base filename + auto setBase = [](CaptureTrigger* pCT, const std::string& name) { pCT->mBaseFilename = name; }; + ct.func_(kBaseFilename.c_str(), setBase); + auto printBase = [](CaptureTrigger* pCT) {pybind11::print(pCT->mBaseFilename); }; + ct.func_(kBaseFilename.c_str(), printBase); + } + + std::string CaptureTrigger::getScript(const std::string& var) + { + std::string s; + s += Scripting::makeMemberFunc(var, kOutputDir, filenameString(mOutputDir, false)); + s += Scripting::makeMemberFunc(var, kBaseFilename, mBaseFilename); + return s; + } + + std::string CaptureTrigger::getOutputNamePrefix(const std::string& output) const + { + auto outDir = std::filesystem::path(mOutputDir); + if (outDir.is_absolute() == false) outDir = std::filesystem::absolute(getExecutableDirectory() + "/" + outDir.string()); + std::string absPath = outDir.string(); + std::string filename = absPath + "/" + mBaseFilename + "." + output + "."; + return filename; + } +} diff --git a/Source/Mogwai/Extensions/Capture/CaptureTrigger.h b/Source/Mogwai/Extensions/Capture/CaptureTrigger.h new file mode 100644 index 000000000..5271c61b3 --- /dev/null +++ b/Source/Mogwai/Extensions/Capture/CaptureTrigger.h @@ -0,0 +1,72 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "../../Mogwai.h" + +namespace Mogwai +{ + class CaptureTrigger : public Extension + { + public: + virtual ~CaptureTrigger() = 0 {} + + virtual void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override final; + virtual void endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override final; + virtual void scriptBindings(Bindings& bindings) override; + virtual void activeGraphChanged(RenderGraph* pNewGraph, RenderGraph* pPrevGraph) override; + protected: + CaptureTrigger(Renderer* pRenderer); + const Renderer* mpRenderer; + using Range = std::pair; // Start frame and count + + virtual void beginRange(RenderGraph* pGraph, const Range& r) {}; + virtual void triggerFrame(RenderContext* pCtx, RenderGraph* pGraph, uint64_t frameID) {}; + virtual void endRange(RenderGraph* pGraph, const Range& r) {}; + + void addRange(const RenderGraph* pGraph, uint64_t startFrame, uint64_t count); + void reset(const RenderGraph* pGraph = nullptr); + void renderUI(Gui::Window& w); + void setOutputDirectory(const std::string& outDir); + std::string getScript(const std::string& var); + std::string getOutputNamePrefix(const std::string& output) const; + + using range_vec = std::vector; + std::unordered_map mGraphRanges; + + std::string mBaseFilename = "Mogwai"; + std::string mOutputDir = "."; + bool mAbsolutePath = false; + bool mShowUI = false; + + struct + { + RenderGraph* pGraph = nullptr; + Range range; + } mCurrent; + }; +} diff --git a/Source/Mogwai/Extensions/Capture/FrameCapture.cpp b/Source/Mogwai/Extensions/Capture/FrameCapture.cpp new file mode 100644 index 000000000..74db2cc43 --- /dev/null +++ b/Source/Mogwai/Extensions/Capture/FrameCapture.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "FrameCapture.h" +#include + +namespace Mogwai +{ + namespace + { + const std::string kPrintFrames = "print"; + const std::string kFrames = "frames"; + const std::string kScriptVar = "fc"; + const std::string kUI = "ui"; + const std::string kOutputs = "outputs"; + const std::string kCapture = "capture"; + + template + std::vector getFirstOfPair(const T& pair) + { + std::vector v; + v.reserve(pair.size()); + for (auto p : pair) v.push_back(p.first); + return v; + } + } + + MOGWAI_EXTENSION(FrameCapture); + + FrameCapture::UniquePtr FrameCapture::create(Renderer* pRenderer) + { + return UniquePtr(new FrameCapture(pRenderer)); + } + + void FrameCapture::renderUI(Gui* pGui) + { + if (mShowUI) + { + auto w = Gui::Window(pGui, "Frame Capture", mShowUI, {}, { 400, 400 }); + CaptureTrigger::renderUI(w); + } + } + + void FrameCapture::scriptBindings(Bindings& bindings) + { + CaptureTrigger::scriptBindings(bindings); + auto& m = bindings.getModule(); + auto fc = m.class_("FrameCapture"); + bindings.addGlobalObject(kScriptVar, this, "Frame Capture Helpers"); + + // Members + fc.func_(kFrames.c_str(), ScriptBindings::overload_cast(&FrameCapture::frames)); + fc.func_(kFrames.c_str(), ScriptBindings::overload_cast(&FrameCapture::frames)); + + auto printGraph = [](FrameCapture* pFC, RenderGraph* pGraph) { pybind11::print(pFC->graphFramesStr(pGraph)); }; + fc.func_(kPrintFrames.c_str(), printGraph); + fc.func_(kCapture.c_str(), &FrameCapture::capture); + auto printAllGraphs = [](FrameCapture* pFC) + { + std::string s; + for (const auto& g : pFC->mGraphRanges) {s += "`" + g.first->getName() + "`:\n" + pFC->graphFramesStr(g.first) + "\n";} + pybind11::print(s.empty() ? "Empty" : s); + }; + fc.func_(kPrintFrames.c_str(), printAllGraphs); + + // Settings + auto showUI = [](FrameCapture* pFC, bool show) { pFC->mShowUI = show; }; + fc.func_(kUI.c_str(), showUI, "show"_a = true); + } + + std::string FrameCapture::getScript() + { + std::string s; + + s += "# Frame Capture\n"; + s += CaptureTrigger::getScript(kScriptVar); + + for (const auto& g : mGraphRanges) + { + s += Scripting::makeMemberFunc(kScriptVar, kFrames, g.first->getName(), getFirstOfPair(g.second)); + } + return s; + } + + void FrameCapture::triggerFrame(RenderContext* pCtx, RenderGraph* pGraph, uint64_t frameID) + { + for (uint32_t i = 0 ; i < pGraph->getOutputCount() ; i++) + { + Texture* pTex = pGraph->getOutput(i)->asTexture().get(); + assert(pTex); + std::string filename = getOutputNamePrefix(pGraph->getOutputName(i)) + to_string(gpFramework->getGlobalClock().frame()) + ".";; + auto ext = Bitmap::getFileExtFromResourceFormat(pTex->getFormat()); + filename += ext; + auto format = Bitmap::getFormatFromFileExtension(ext); + pTex->captureToFile(0, 0, filename, format); + } + } + + void FrameCapture::frames(const RenderGraph* pGraph, const uint64_vec& frames) + { + for (auto f : frames) addRange(pGraph, f, 1); + } + + void FrameCapture::frames(const std::string& graphName, const uint64_vec& frames) + { + auto pGraph = mpRenderer->getGraph(graphName).get(); + if (!pGraph) throw std::runtime_error("Can't find a graph named `" + graphName + "`"); + this->frames(pGraph, frames); + } + + std::string FrameCapture::graphFramesStr(const RenderGraph* pGraph) + { + const auto& ranges = mGraphRanges[pGraph]; + std::string s("\t"); + s += kFrames + " = " + to_string(getFirstOfPair(ranges)); + return s; + } + + void FrameCapture::capture() + { + auto pGraph = mpRenderer->getActiveGraph(); + if (!pGraph) return; + uint64_t frameID = gpFramework->getGlobalClock().frame(); + triggerFrame(gpDevice->getRenderContext(), pGraph, frameID); + } +} diff --git a/Source/Mogwai/Extensions/Capture/FrameCapture.h b/Source/Mogwai/Extensions/Capture/FrameCapture.h new file mode 100644 index 000000000..54ab4fcb8 --- /dev/null +++ b/Source/Mogwai/Extensions/Capture/FrameCapture.h @@ -0,0 +1,51 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "../../Mogwai.h" +#include "CaptureTrigger.h" + +namespace Mogwai +{ + class FrameCapture : public CaptureTrigger + { + public: + static UniquePtr create(Renderer* pRenderer); + virtual void renderUI(Gui* pGui) override; + virtual void scriptBindings(Bindings& bindings) override; + virtual std::string getScript() override; + virtual void triggerFrame(RenderContext* pCtx, RenderGraph* pGraph, uint64_t frameID); + void capture(); + private: + FrameCapture(Renderer* pRenderer) : CaptureTrigger(pRenderer) {} + bool mShowUI = false; + using uint64_vec = std::vector; + void frames(const RenderGraph* pGraph, const uint64_vec& frames); + void frames(const std::string& graphName, const uint64_vec& frames); + std::string graphFramesStr(const RenderGraph* pGraph); + }; +} diff --git a/Source/Mogwai/Extensions/Capture/VideoCapture.cpp b/Source/Mogwai/Extensions/Capture/VideoCapture.cpp new file mode 100644 index 000000000..752fa250b --- /dev/null +++ b/Source/Mogwai/Extensions/Capture/VideoCapture.cpp @@ -0,0 +1,219 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "VideoCapture.h" + +namespace Mogwai +{ + namespace + { + const std::string kScriptVar = "vc"; + const std::string kUI = "ui"; + const std::string kCodec = "codec"; + const std::string kFps = "fps"; + const std::string kBitrate = "bitrate"; + const std::string kGopSize = "gopSize"; + const std::string kRanges = "ranges"; + const std::string kPrint = "print"; + const std::string kOutputs = "outputs"; + + Texture::SharedPtr createTextureForBlit(const Texture* pSource) + { + assert(pSource->getType() == Texture::Type::Texture2D); + return Texture::create2D(pSource->getWidth(), pSource->getHeight(), ResourceFormat::RGBA8UnormSrgb, 1, 1, nullptr, Texture::BindFlags::RenderTarget); + } + } + + MOGWAI_EXTENSION(VideoCapture); + + VideoCapture::VideoCapture(Renderer* pRenderer) : CaptureTrigger(pRenderer) + { + mpEncoderUI = VideoEncoderUI::create(nullptr, nullptr); + } + + VideoCapture::UniquePtr VideoCapture::create(Renderer* pRenderer) + { + return UniquePtr(new VideoCapture(pRenderer)); + } + + void VideoCapture::renderUI(Gui* pGui) + { + if (mShowUI) + { + auto w = Gui::Window(pGui, "Video Capture", mShowUI, {}, { 800, 400 }); + CaptureTrigger::renderUI(w); + w.separator(); + mpEncoderUI->render(w, true); + } + } + + void VideoCapture::beginRange(RenderGraph* pGraph, const Range& r) + { + VideoEncoder::Desc d; + d.bitrateMbps = mpEncoderUI->getBitrate(); + d.codec = mpEncoderUI->getCodec(); + d.fps = mpEncoderUI->getFPS(); + d.gopSize = mpEncoderUI->getGopSize(); + + for(uint32_t i = 0 ; i < pGraph->getOutputCount() ; i++) + { + const auto& outputName = pGraph->getOutputName(i); + Texture::SharedPtr pTex = pGraph->getOutput(i)->asTexture(); + if (!pTex || pTex->getType() != Texture::Type::Texture2D) + { + logError("Can't video capture " + outputName + ". The output is not a Texture2D"); + continue; + } + + EncodeData encoder; + auto texFormat = pTex->getFormat(); + if (VideoEncoder::isFormatSupported(texFormat) == false) + { + auto res = msgBox("Trying to record graph output " + outputName + " but the resource format is not supported by the video encoder.\nWould you like to capture the output as an RGBA8Srgb resource?\n\nFor HDR textures, this operation will clamp the results", MsgBoxType::YesNo); + if(res == MsgBoxButton::No) continue; + encoder.pBlitTex = createTextureForBlit(pTex.get()); + pTex = encoder.pBlitTex; + } + + d.height = pTex->getHeight(); + d.width = pTex->getWidth(); + d.format = pTex->getFormat(); + d.filename = getOutputNamePrefix(outputName) + to_string(r.first) + "." + to_string(r.second) + "." + VideoEncoder::getSupportedContainerForCodec(d.codec)[0].ext; + encoder.output = outputName; + encoder.pEncoder = VideoEncoder::create(d); + mEncoders.push_back(std::move(encoder)); + } + } + + void VideoCapture::endRange(RenderGraph* pGraph, const Range& r) + { + for (const auto& e : mEncoders) e.pEncoder->endCapture(); + } + + void VideoCapture::triggerFrame(RenderContext* pCtx, RenderGraph* pGraph, uint64_t frameID) + { + for (const auto& e : mEncoders) + { + Texture::SharedPtr pTex = std::dynamic_pointer_cast(pGraph->getOutput(e.output)); + if (e.pBlitTex) + { + pCtx->blit(pTex->getSRV(0, 1, 0, 1), e.pBlitTex->getRTV(0, 0, 1)); + pTex = e.pBlitTex; + } + + e.pEncoder->appendFrame(pCtx->readTextureSubresource(pTex.get(), 0).data()); + } + } + + void VideoCapture::scriptBindings(Bindings& bindings) + { + CaptureTrigger::scriptBindings(bindings); + auto& m = bindings.getModule(); + auto vc = m.class_("VideoCapture"); + bindings.addGlobalObject(kScriptVar, this, "Video Capture Helpers"); + + // UI + auto showUI = [](VideoCapture* pFC, bool show) { pFC->mShowUI = show; }; + vc.func_(kUI.c_str(), showUI, "show"_a = true); + + // Settings + auto getCodec = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getCodec(); }; + auto setCodec = [](VideoCapture* pVC, VideoEncoder::Codec c) {pVC->mpEncoderUI->setCodec(c); return pVC; }; + vc.func_(kCodec.c_str(), getCodec); + vc.func_(kCodec.c_str(), setCodec); + + auto getFPS = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getFPS(); }; + auto setFPS = [](VideoCapture* pVC, uint32_t fps) {pVC->mpEncoderUI->setFPS(fps); return pVC; }; + vc.func_(kFps.c_str(), getFPS); + vc.func_(kFps.c_str(), setFPS); + + auto getBitrate = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getBitrate(); }; + auto setBitrate = [](VideoCapture* pVC, float bitrate) {pVC->mpEncoderUI->setBitrate(bitrate); return pVC; }; + vc.func_(kBitrate.c_str(), getBitrate); + vc.func_(kBitrate.c_str(), setBitrate); + + auto getGopSize = [](VideoCapture* pVC) {return pVC->mpEncoderUI->getGopSize(); }; + auto setGopSize = [](VideoCapture* pVC, uint32_t gop) {pVC->mpEncoderUI->setGopSize(gop); return pVC; }; + vc.func_(kGopSize.c_str(), getGopSize); + vc.func_(kGopSize.c_str(), setGopSize); + + // Ranges + vc.func_(kRanges.c_str(), ScriptBindings::overload_cast(&VideoCapture::ranges)); + vc.func_(kRanges.c_str(), ScriptBindings::overload_cast(&VideoCapture::ranges)); + + auto printGraph = [](VideoCapture* pVC, RenderGraph* pGraph) { pybind11::print(pVC->graphRangesStr(pGraph)); }; + vc.func_(kPrint.c_str(), printGraph); + + auto printAllGraphs = [](VideoCapture* pVC) + { + std::string s; + for (const auto& g : pVC->mGraphRanges) { s += "`" + g.first->getName() + "`:\n" + pVC->graphRangesStr(g.first) + "\n"; } + pybind11::print(s.empty() ? "Empty" : s); + }; + vc.func_(kPrint.c_str(), printAllGraphs); + } + + std::string VideoCapture::getScript() + { + if (mGraphRanges.empty()) return ""; + + std::string s("# Video Capture\n"); + s += CaptureTrigger::getScript(kScriptVar); + s += Scripting::makeMemberFunc(kScriptVar, kCodec, mpEncoderUI->getCodec()); + s += Scripting::makeMemberFunc(kScriptVar, kFps, mpEncoderUI->getFPS()); + s += Scripting::makeMemberFunc(kScriptVar, kBitrate, mpEncoderUI->getBitrate()); + s += Scripting::makeMemberFunc(kScriptVar, kGopSize, mpEncoderUI->getGopSize()); + + for (const auto& g : mGraphRanges) + { + s += Scripting::makeMemberFunc(kScriptVar, kRanges, g.first->getName(), g.second); + } + return s; + } + + void VideoCapture::ranges(const RenderGraph* pGraph, const range_vec& ranges) + { + for (auto r : ranges) addRange(pGraph, r.first, r.second); + } + + void VideoCapture::ranges(const std::string& graphName, const range_vec& ranges) + { + auto pGraph = mpRenderer->getGraph(graphName).get(); + if (!pGraph) throw std::runtime_error("Can't find a graph named `" + graphName + "`"); + this->ranges(pGraph, ranges); + } + + std::string VideoCapture::graphRangesStr(const RenderGraph* pGraph) + { + const auto& g = mGraphRanges[pGraph]; + std::string s("\t"); + s += kRanges + " = " + to_string(g); + return s; + } +} + diff --git a/Source/Mogwai/Extensions/Capture/VideoCapture.h b/Source/Mogwai/Extensions/Capture/VideoCapture.h new file mode 100644 index 000000000..9cb51e9d9 --- /dev/null +++ b/Source/Mogwai/Extensions/Capture/VideoCapture.h @@ -0,0 +1,63 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "CaptureTrigger.h" +#include "Utils/Video/VideoEncoderUI.h" +#include "Utils/Video/VideoEncoder.h" + +namespace Mogwai +{ + class VideoCapture : public CaptureTrigger + { + public: + static UniquePtr create(Renderer* pRenderer); + virtual void renderUI(Gui* pGui) override; + virtual void beginRange(RenderGraph* pGraph, const Range& r) override; + virtual void endRange(RenderGraph* pGraph, const Range& r) override; + virtual void scriptBindings(Bindings& bindings) override; + virtual std::string getScript() override; + virtual void triggerFrame(RenderContext* pCtx, RenderGraph* pGraph, uint64_t frameID) override; + + private: + VideoCapture(Renderer* pRenderer); + bool mShowUI = false; + VideoEncoderUI::UniquePtr mpEncoderUI; + + void ranges(const RenderGraph* pGraph, const range_vec& ranges); + void ranges(const std::string& graphName, const range_vec& ranges); + std::string graphRangesStr(const RenderGraph* pGraph); + + struct EncodeData + { + std::string output; + VideoEncoder::UniquePtr pEncoder; + Texture::SharedPtr pBlitTex; + }; + std::vector mEncoders; + }; +} diff --git a/Source/Mogwai/Mogwai.cpp b/Source/Mogwai/Mogwai.cpp new file mode 100644 index 000000000..002192172 --- /dev/null +++ b/Source/Mogwai/Mogwai.cpp @@ -0,0 +1,602 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "Mogwai.h" +#include "MogwaiSettings.h" +#include +#include + +namespace Mogwai +{ + namespace + { + std::map* gExtensions; // Map ensures ordering + + const std::string gkDefaultScene = "Arcade/Arcade.fscene"; + const char* kEditorExecutableName = "RenderGraphEditor"; + const char* kEditorSwitch = "editor"; + const char* kOutfileDirSwitch = "outputdir"; + const char* kScriptSwitch = "script"; + const char* kGraphFileSwitch = "graphFile"; + const char* kGraphNameSwitch = "graphName"; + } + + size_t Renderer::DebugWindow::index = 0; + + void Renderer::extend(Extension::CreateFunc func, const std::string& name) + { + if (!gExtensions) gExtensions = new std::map(); + if (gExtensions->find(name) != gExtensions->end()) + { + logError("Extension " + name + " already registered. If you continue the new extension will be discarded"); + return; + } + (*gExtensions)[name] = func; + } + + void Renderer::onShutdown() + { + resetEditor(); + gpDevice->flushAndSync(); // Need to do that because clearing the graphs will try to release some state objects which might be in use + mGraphs.clear(); + } + + void Renderer::onLoad(RenderContext* pRenderContext) + { + mpExtensions.push_back(MogwaiSettings::create(this)); + if(gExtensions) + { + for (auto& f : (*gExtensions)) mpExtensions.push_back(f.second(this)); + safe_delete(gExtensions); + } + + auto regBinding = [this](ScriptBindings::Module& m) {this->registerScriptBindings(m); }; + ScriptBindings::registerBinding(regBinding); + + // If editor opened from running render graph, get the name of the file to read + if (gpFramework->getArgList().argExists(kScriptSwitch)) loadScript(gpFramework->getArgList()[kScriptSwitch].asString()); + } + + RenderGraph* Renderer::getActiveGraph() const + { + return mGraphs.size() ? mGraphs[mActiveGraph].pGraph.get() : nullptr; + } + + void Renderer::onGuiRender(Gui* pGui) + { + for (auto& pe : mpExtensions) pe->renderUI(pGui); + } + + bool isInVector(const std::vector& strVec, const std::string& str) + { + return std::find(strVec.begin(), strVec.end(), str) != strVec.end(); + } + + Gui::DropdownList createDropdownFromVec(const std::vector& strVec, const std::string& currentLabel) + { + Gui::DropdownList dropdown; + for (size_t i = 0; i < strVec.size(); i++) dropdown.push_back({ (uint32_t)i, strVec[i] }); + return dropdown; + } + + void Renderer::addDebugWindow() + { + DebugWindow window; + window.windowName = "Debug Window " + std::to_string(DebugWindow::index++); + window.currentOutput = mGraphs[mActiveGraph].mainOutput; + markOutput(window.currentOutput); + mGraphs[mActiveGraph].debugWindows.push_back(window); + } + + void Renderer::unmarkOutput(const std::string& name) + { + auto& graphData = mGraphs[mActiveGraph]; + // Skip the original outputs + if (isInVector(graphData.originalOutputs, name)) return; + + // Decrease the reference counter + auto& ref = graphData.graphOutputRefs.at(name); + ref--; + if (ref == 0) + { + graphData.graphOutputRefs.erase(name); + graphData.pGraph->unmarkOutput(name); + } + } + + void Renderer::markOutput(const std::string& name) + { + auto& graphData = mGraphs[mActiveGraph]; + // Skip the original outputs + if (isInVector(graphData.originalOutputs, name)) return; + auto& refVec = mGraphs[mActiveGraph].graphOutputRefs; + refVec[name]++; + if (refVec[name] == 1) mGraphs[mActiveGraph].pGraph->markOutput(name); + } + + void Renderer::renderOutputUI(Gui::Widgets& widget, const Gui::DropdownList& dropdown, std::string& selectedOutput) + { + uint32_t activeOut = -1; + for (size_t i = 0; i < dropdown.size(); i++) + { + if (dropdown[i].label == selectedOutput) + { + activeOut = (uint32_t)i; + break; + } + } + + // This can happen when `showAllOutputs` changes to false, and the chosen output is not an original output. We will force an output change + bool forceOutputChange = activeOut == -1; + if (forceOutputChange) activeOut = 0; + + if (widget.dropdown("Output", dropdown, activeOut) || forceOutputChange) + { + // Unmark old output, set new output, mark new output + unmarkOutput(selectedOutput); + selectedOutput = dropdown[activeOut].label; + markOutput(selectedOutput); + } + } + + bool Renderer::renderDebugWindow(Gui::Widgets& widget, const Gui::DropdownList& dropdown, DebugWindow& data, const uvec2& winSize) + { + // Get the current output, in case `renderOutputUI()` unmarks it + Texture::SharedPtr pTex = std::dynamic_pointer_cast(mGraphs[mActiveGraph].pGraph->getOutput(data.currentOutput)); + std::string label = data.currentOutput + "##" + mGraphs[mActiveGraph].pGraph->getName(); + if (!pTex) { logError("Invalid output resource. Is not a texture."); } + + uvec2 debugSize = (uvec2)(vec2(winSize) * vec2(0.4f, 0.55f)); + uvec2 debugPos = winSize - debugSize; + debugPos -= 10; + + // Display the dropdown + Gui::Window debugWindow(widget.gui(), data.windowName.c_str(), debugSize, debugPos); + if (debugWindow.gui()) + { + if (debugWindow.button("Save To File", true)) Bitmap::saveImageDialog(pTex.get()); + renderOutputUI(widget, dropdown, data.currentOutput); + debugWindow.separator(); + + debugWindow.image(label.c_str(), pTex); + debugWindow.release(); + return true; + } + + return false; + } + + void Renderer::eraseDebugWindow(size_t id) + { + unmarkOutput(mGraphs[mActiveGraph].debugWindows[id].currentOutput); + mGraphs[mActiveGraph].debugWindows.erase(mGraphs[mActiveGraph].debugWindows.begin() + id); + } + + void Renderer::graphOutputsGui(Gui::Widgets& widget) + { + RenderGraph::SharedPtr pGraph = mGraphs[mActiveGraph].pGraph; + if (mGraphs[mActiveGraph].debugWindows.size()) mGraphs[mActiveGraph].showAllOutputs = true; + auto strVec = mGraphs[mActiveGraph].showAllOutputs ? pGraph->getAvailableOutputs() : mGraphs[mActiveGraph].originalOutputs; + Gui::DropdownList graphOuts = createDropdownFromVec(strVec, mGraphs[mActiveGraph].mainOutput); + + widget.checkbox("List All Outputs", mGraphs[mActiveGraph].showAllOutputs); + widget.tooltip("Display every possible output in the render-graph, even if it wasn't explicitly marked as one. If there's a debug window open, you won't be able to uncheck this"); + + if (graphOuts.size()) + { + uvec2 dims(gpFramework->getTargetFbo()->getWidth(), gpFramework->getTargetFbo()->getHeight()); + + for (size_t i = 0; i < mGraphs[mActiveGraph].debugWindows.size();) + { + if (renderDebugWindow(widget, graphOuts, mGraphs[mActiveGraph].debugWindows[i], dims) == false) + { + eraseDebugWindow(i); + } + else i++; + } + + renderOutputUI(widget, graphOuts, mGraphs[mActiveGraph].mainOutput); + + // Render the debug windows *before* adding/removing debug windows + if (widget.button("Show In Debug Window")) addDebugWindow(); + if (mGraphs[mActiveGraph].debugWindows.size()) + { + if (widget.button("Close all debug windows")) + { + while (mGraphs[mActiveGraph].debugWindows.size()) eraseDebugWindow(0); + } + } + } + } + + void Renderer::onDroppedFile(const std::string& filename) + { + std::string ext = getExtensionFromFile(filename); + if (std::any_of(Scene::kFileExtensionFilters.begin(), Scene::kFileExtensionFilters.end(), [&ext](FileDialogFilter f) {return f.ext == ext; })) loadScene(filename); + else if (ext == "py") loadScript(filename); + else logWarning("RenderGraphViewer::onDroppedFile() - Unknown file extension `" + ext + "`"); + } + + void Renderer::editorFileChangeCB() + { + mEditorScript = readFile(mEditorTempFile); + } + + void Renderer::openEditor() + { + bool unmarkOut = (isInVector(mGraphs[mActiveGraph].originalOutputs, mGraphs[mActiveGraph].mainOutput) == false); + // If the current graph output is not an original output, unmark it + if (unmarkOut) mGraphs[mActiveGraph].pGraph->unmarkOutput(mGraphs[mActiveGraph].mainOutput); + + mEditorTempFile = getTempFilename(); + + // Save the graph + RenderGraphExporter::save(mGraphs[mActiveGraph].pGraph, mEditorTempFile); + + // Register an update callback + monitorFileUpdates(mEditorTempFile, std::bind(&Renderer::editorFileChangeCB, this)); + + // Run the process + std::string commandLineArgs = '-' + std::string(kEditorSwitch) + " -" + std::string(kGraphFileSwitch); + commandLineArgs += ' ' + mEditorTempFile + " -" + std::string(kGraphNameSwitch) + ' ' + mGraphs[mActiveGraph].pGraph->getName(); + mEditorProcess = executeProcess(kEditorExecutableName, commandLineArgs); + + // Mark the output if it's required + if (unmarkOut) mGraphs[mActiveGraph].pGraph->markOutput(mGraphs[mActiveGraph].mainOutput); + } + + void Renderer::resetEditor() + { + if (mEditorProcess) + { + closeSharedFile(mEditorTempFile); + std::remove(mEditorTempFile.c_str()); + if (mEditorProcess != kInvalidProcessId) + { + terminateProcess(mEditorProcess); + mEditorProcess = 0; + } + } + } + + void Renderer::setActiveGraph(uint32_t active) + { + RenderGraph* pOld = getActiveGraph(); + mActiveGraph = active; + RenderGraph* pNew = getActiveGraph(); + if (pOld != pNew) + { + for (auto& e : mpExtensions) e->activeGraphChanged(pNew, pOld); + } + } + + void Renderer::removeGraph(const RenderGraph::SharedPtr& pGraph) + { + for (auto& e : mpExtensions) e->removeGraph(pGraph.get()); + size_t i = 0; + for (; i < mGraphs.size(); i++) if (mGraphs[i].pGraph == pGraph) break; + assert(i < mGraphs.size()); + mGraphs.erase(mGraphs.begin() + i); + if (mActiveGraph >= i && mActiveGraph > 0) mActiveGraph--; + setActiveGraph(mActiveGraph); + } + + void Renderer::removeGraph(const std::string& graphName) + { + auto pGraph = getGraph(graphName); + if (pGraph) removeGraph(pGraph); + else msgBox("Can't find a graph named `" + graphName + "`. There's nothing to remove"); + } + + RenderGraph::SharedPtr Renderer::getGraph(const std::string& graphName) const + { + for (const auto& g : mGraphs) + { + if (g.pGraph->getName() == graphName) return g.pGraph; + } + return nullptr; + } + + void Renderer::removeActiveGraph() + { + if (mGraphs.size()) removeGraph(mGraphs[mActiveGraph].pGraph); + } + + std::vector Renderer::getGraphOutputs(const RenderGraph::SharedPtr& pGraph) + { + std::vector outputs; + for (size_t i = 0; i < pGraph->getOutputCount(); i++) outputs.push_back(pGraph->getOutputName(i)); + return outputs; + } + + void Renderer::initGraph(const RenderGraph::SharedPtr& pGraph, GraphData* pData) + { + if (!pData) + { + mGraphs.push_back({}); + pData = &mGraphs.back(); + } + + GraphData& data = *pData; + // Set input image if it exists + data.pGraph = pGraph; + if (!mpScene) loadScene(gkDefaultScene); + data.pGraph->setScene(mpScene); + if (data.pGraph->getOutputCount() != 0) data.mainOutput = data.pGraph->getOutputName(0); + + // Store the original outputs + data.originalOutputs = getGraphOutputs(pGraph); + + for (auto& e : mpExtensions) e->addGraph(pGraph.get()); + } + + void Renderer::loadScriptDialog() + { + openFileDialog(Scripting::kFileExtensionFilters, mScriptFilename); + } + + void Renderer::loadScript(const std::string& filename) + { + assert(filename.size()); + + try + { + auto pBar = ProgressBar::show("Loading Configuration"); + auto c = Scripting::getGlobalContext(); + Scripting::runScriptFromFile(filename, c); + } + catch (std::exception e) + { + logError("Error when loading configuration file.\n" + std::string(e.what())); + } + } + + void Renderer::addGraph(const RenderGraph::SharedPtr& pGraph) + { + if (pGraph == nullptr) + { + msgBox("Can't add an empty graph", MsgBoxType::Ok); + return; + } + + // If a graph with the same name already exists, remove it + GraphData* pGraphData = nullptr; + for (size_t i = 0; i < mGraphs.size(); i++) + { + if (mGraphs[i].pGraph->getName() == pGraph->getName()) + { + if (msgBox("Graph `" + pGraph->getName() + "` already exists. Replace it?", MsgBoxType::YesNo) == MsgBoxButton::No) return; + pGraphData = &mGraphs[i]; + break; + } + } + initGraph(pGraph, pGraphData); + } + + void Renderer::loadScene(std::string filename) + { + if (filename.empty()) + { + if (!openFileDialog(Scene::kFileExtensionFilters, filename)) return; + } + +#ifdef FALCOR_D3D12 + mpScene = SceneBuilder::create(filename)->getScene(); +#else + mpScene = Scene::loadFromFile(filename); +#endif + const auto& pFbo = gpFramework->getTargetFbo(); + float ratio = float(pFbo->getWidth()) / float(pFbo->getHeight()); + if (mpScene) + { + mpScene->setCameraAspectRatio(ratio); + if (mpSampler == nullptr) + { + // create common texture sampler + Sampler::Desc desc; + desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + desc.setMaxAnisotropy(8); + mpSampler = Sampler::create(desc); + } + mpScene->bindSamplerToMaterials(mpSampler); + } + + for (auto& g : mGraphs) g.pGraph->setScene(mpScene); + gpFramework->getGlobalClock().now(0); + } + + Scene::SharedPtr Renderer::getScene() const + { + return mpScene; + } + + void Renderer::applyEditorChanges() + { + if (!mEditorProcess) return; + // If the editor was closed, reset the handles + if ((mEditorProcess != kInvalidProcessId) && isProcessRunning(mEditorProcess) == false) resetEditor(); + + if (mEditorScript.empty()) return; + + // Unmark the current output if it wasn't an original one + bool unmarkOut = (isInVector(mGraphs[mActiveGraph].originalOutputs, mGraphs[mActiveGraph].mainOutput) == false); + if (unmarkOut) mGraphs[mActiveGraph].pGraph->unmarkOutput(mGraphs[mActiveGraph].mainOutput); + + // Run the scripting + Scripting::getGlobalContext().setObject("g", mGraphs[mActiveGraph].pGraph); + Scripting::runScript(mEditorScript); + + // Update the original output list + mGraphs[mActiveGraph].originalOutputs = getGraphOutputs(mGraphs[mActiveGraph].pGraph); + + // Mark the current output if it's required + if (unmarkOut) mGraphs[mActiveGraph].pGraph->markOutput(mGraphs[mActiveGraph].mainOutput); + + mEditorScript.clear(); + } + + void Renderer::executeActiveGraph(RenderContext* pRenderContext) + { + if (mGraphs.empty()) return; + auto& pGraph = mGraphs[mActiveGraph].pGraph; + + // Execute graph. + (*pGraph->getPassesDictionary())[kRenderPassRefreshFlags] = (uint32_t)RenderPassRefreshFlags::None; + pGraph->execute(pRenderContext); + } + + void Renderer::startFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + { + for (auto& pe : mpExtensions) pe->beginFrame(pRenderContext, pTargetFbo); + } + + void Renderer::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + { + if(mScriptFilename.size()) + { + std::string s = mScriptFilename; + mScriptFilename.clear(); + loadScript(s); + } + + startFrame(pRenderContext, pTargetFbo); + applyEditorChanges(); + + // Clear frame buffer. + const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); + pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); + + if (mGraphs.size()) + { + auto& pGraph = mGraphs[mActiveGraph].pGraph; + + // Update scene and camera. + if (mpScene) + { + mpScene->update(pRenderContext, gpFramework->getGlobalClock().now()); + } + + executeActiveGraph(pRenderContext); + + // Blit main graph output to frame buffer. + if (mGraphs[mActiveGraph].mainOutput.size()) + { + Texture::SharedPtr pOutTex = std::dynamic_pointer_cast(pGraph->getOutput(mGraphs[mActiveGraph].mainOutput)); + assert(pOutTex); + pRenderContext->blit(pOutTex->getSRV(), pTargetFbo->getRenderTargetView(0)); + } + } + + for (auto& pe : mpExtensions) pe->endFrame(pRenderContext, pTargetFbo); + } + + bool Renderer::onMouseEvent(const MouseEvent& mouseEvent) + { + for (auto& pe : mpExtensions) + { + if (pe->mouseEvent(mouseEvent)) return true; + } + + if (mGraphs.size()) mGraphs[mActiveGraph].pGraph->onMouseEvent(mouseEvent); + return mpScene ? mpScene->onMouseEvent(mouseEvent) : false; + } + + bool Renderer::onKeyEvent(const KeyboardEvent& keyEvent) + { + for (auto& pe : mpExtensions) + { + if (pe->keyboardEvent(keyEvent)) return true; + } + if (mGraphs.size()) mGraphs[mActiveGraph].pGraph->onKeyEvent(keyEvent); + return mpScene ? mpScene->onKeyEvent(keyEvent) : false; + } + + void Renderer::onResizeSwapChain(uint32_t width, uint32_t height) + { + for (auto& g : mGraphs) + { + g.pGraph->onResize(gpFramework->getTargetFbo().get()); + Scene::SharedPtr graphScene = g.pGraph->getScene(); + if (graphScene) graphScene->setCameraAspectRatio((float)width / (float)height); + } + if (mpScene) mpScene->setCameraAspectRatio((float)width / (float)height); + } + + void Renderer::onDataReload() + { + RenderPassLibrary::instance().reloadLibraries(gpFramework->getRenderContext()); + } + + size_t Renderer::findGraph(std::string_view name) + { + for (size_t i = 0; i < mGraphs.size(); i++) + { + if (mGraphs[i].pGraph->getName() == name) return i; + }; + return -1; + } + + std::string Renderer::getVersionString() + { + return "Mogwai " + to_string(kMajorVersion) + "." + to_string(kMinorVersion); + } +} + +#ifdef _WIN32 +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) +#else +int main(int argc, char** argv) +#endif +{ + try + { + msgBoxTitle("Mogwai"); + + IRenderer::UniquePtr pRenderer = std::make_unique(); + SampleConfig config; + config.windowDesc.title = "Mogwai"; + + ArgList args; +#ifdef _WIN32 + args.parseCommandLine(GetCommandLineA()); + int argc = 0; + char** argv = nullptr; +#else + args.parseCommandLine(argc, argv); + config.argc = argc; + config.argv = argv; +#endif + + Sample::run(config, pRenderer, argc, argv); + } + catch (std::exception e) + { + msgBox("Mogwai crashed unexpectedly...\n" + std::string(e.what())); + } + return 0; +} diff --git a/Source/Mogwai/Mogwai.h b/Source/Mogwai/Mogwai.h new file mode 100644 index 000000000..6f7ffd32e --- /dev/null +++ b/Source/Mogwai/Mogwai.h @@ -0,0 +1,184 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "FalcorExperimental.h" + +using namespace Falcor; + +namespace Mogwai +{ + class Renderer; + class Extension + { + public: + class Bindings + { + public: + ScriptBindings::Module& getModule() { return mModule; } + ScriptBindings::Class& getMogwaiClass() { return mMogwai; } + template + void addGlobalObject(const std::string& name, const T& obj, const std::string& desc) + { + if (mGlobalObjects.find(name) != mGlobalObjects.end()) throw std::exception(("Object `" + name + "` already exists").c_str()); + Scripting::getGlobalContext().setObject(name, obj); + mGlobalObjects[name] = desc; + } + + private: + Bindings(ScriptBindings::Module& m, ScriptBindings::Class& c) : mModule(m), mMogwai(c) {} + friend class Renderer; + std::unordered_map mGlobalObjects; + ScriptBindings::Module& mModule; + ScriptBindings::Class& mMogwai; + }; + + using UniquePtr = std::unique_ptr; + virtual ~Extension() = default; + + using CreateFunc = UniquePtr(*)(Renderer* pRenderer); + + virtual void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) {}; + virtual void endFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) {}; + virtual void renderUI(Gui* pGui) {}; + virtual bool mouseEvent(const MouseEvent& e) { return false; } + virtual bool keyboardEvent(const KeyboardEvent& e) { return false; } + virtual void scriptBindings(Bindings& bindings) {}; + virtual std::string getScript() { return {}; } + virtual void addGraph(RenderGraph* pGraph) {}; + virtual void removeGraph(RenderGraph* pGraph) {}; + virtual void activeGraphChanged(RenderGraph* pNewGraph, RenderGraph* pPrevGraph) {}; + }; + + class Renderer : public IRenderer + { + public: + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onResizeSwapChain(uint32_t width, uint32_t height) override; + bool onKeyEvent(const KeyboardEvent& e) override; + bool onMouseEvent(const MouseEvent& e) override; + void onGuiRender(Gui* pGui) override; + void onDataReload() override; + void onShutdown() override; + void onDroppedFile(const std::string& filename) override; + void loadScriptDialog(); + void loadScript(const std::string& filename); + void dumpConfig(std::string filename = {}) const; + static std::string getVersionString(); + + static void extend(Extension::CreateFunc func, const std::string& name); + + static constexpr uint32_t kMajorVersion = 0; + static constexpr uint32_t kMinorVersion = 1; + + RenderGraph* getActiveGraph() const; +// private: // MOGWAI + friend class Extension; + std::vector mpExtensions; + + struct DebugWindow + { + std::string windowName; + std::string currentOutput; + static size_t index; + }; + + struct GraphData + { + RenderGraph::SharedPtr pGraph; + std::string mainOutput; + bool showAllOutputs = false; + std::vector originalOutputs; + std::vector debugWindows; + std::unordered_map graphOutputRefs; + }; + + Scene::SharedPtr mpScene; + + void addGraph(const RenderGraph::SharedPtr& pGraph); + void removeGraph(const RenderGraph::SharedPtr& pGraph); + void removeGraph(const std::string& graphName); + RenderGraph::SharedPtr getGraph(const std::string& graphName) const; + void initGraph(const RenderGraph::SharedPtr& pGraph, GraphData* pData); + + void removeActiveGraph(); + void loadScene(std::string filename = ""); + Scene::SharedPtr getScene() const; + void executeActiveGraph(RenderContext* pRenderContext); + void startFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo); + + std::vector getGraphOutputs(const RenderGraph::SharedPtr& pGraph); + void graphOutputsGui(Gui::Widgets& widget); + bool renderDebugWindow(Gui::Widgets& widget, const Gui::DropdownList& dropdown, DebugWindow& data, const uvec2& winSize); // Returns false if the window was closed + void renderOutputUI(Gui::Widgets& widget, const Gui::DropdownList& dropdown, std::string& selectedOutput); + void addDebugWindow(); + void eraseDebugWindow(size_t id); + void unmarkOutput(const std::string& name); + void markOutput(const std::string& name); + size_t findGraph(std::string_view name); + + std::vector mGraphs; + uint32_t mActiveGraph = 0; + Sampler::SharedPtr mpSampler = nullptr; + std::string mScriptFilename; + + // Editor stuff + void openEditor(); + void resetEditor(); + void editorFileChangeCB(); + void applyEditorChanges(); + void setActiveGraph(uint32_t active); + + static const size_t kInvalidProcessId = -1; // We use this to know that the editor was launching the viewer + size_t mEditorProcess = 0; + std::string mEditorTempFile; + std::string mEditorScript; + + // Scripting + void registerScriptBindings(ScriptBindings::Module& m); + std::string mGlobalHelpMessage; + }; + +#define MOGWAI_EXTENSION(Name) \ + struct ExtendRenderer##Name { \ + ExtendRenderer##Name() \ + { \ + Renderer::extend(Name::create, #Name); \ + } \ + } gRendererExtensions##Name; + + constexpr char kRendererVar[] = "m"; // MOGWAI do we want to expose it to all the extensions? + + inline std::string filenameString(const std::string& s, bool stripDataDirs = true) + { + std::string filename = stripDataDirs ? stripDataDirectories(s) : s; + std::replace(filename.begin(), filename.end(), '\\', '/'); + return filename; + } +} diff --git a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj b/Source/Mogwai/Mogwai.vcxproj similarity index 66% rename from Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj rename to Source/Mogwai/Mogwai.vcxproj index 551b239e5..fd13d5ce2 100644 --- a/Samples/RenderGraph/RenderGraphViewer/RenderGraphViewer.vcxproj +++ b/Source/Mogwai/Mogwai.vcxproj @@ -1,4 +1,4 @@ - + @@ -11,25 +11,41 @@ - + + + + + + + + Create + - + + + + + + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + + {204D1CBA-6D34-4EB7-9F78-A1369F8F0F49} Win32Proj - FeatureDemo + Mogwai 10.0.17763.0 - RenderGraphViewer + Mogwai @@ -37,6 +53,7 @@ true v141 Unicode + false Application @@ -49,10 +66,10 @@ - + - + @@ -63,11 +80,13 @@ - - + NotUsing Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions) + stdcpp17 + true + true Windows @@ -77,12 +96,14 @@ Level3 - - + Use MaxSpeed true true WIN32;NDEBUG;%(PreprocessorDefinitions) + stdcpp17 + true + true Windows diff --git a/Source/Mogwai/Mogwai.vcxproj.filters b/Source/Mogwai/Mogwai.vcxproj.filters new file mode 100644 index 000000000..6a4400c29 --- /dev/null +++ b/Source/Mogwai/Mogwai.vcxproj.filters @@ -0,0 +1,57 @@ + + + + + + + + Extensions\Capture + + + Extensions\Capture + + + Extensions\Capture + + + + + + + + Extensions\Capture + + + Extensions\Capture + + + Extensions\Capture + + + + + + {43cd0683-b939-4d18-a231-2ab0053cbf9a} + + + {002e1de0-f9e3-40c6-a920-bc17c9369762} + + + {306d36f4-db42-4d88-ad51-be753440d5fa} + + + + + Data + + + Data + + + Data + + + Data + + + \ No newline at end of file diff --git a/Source/Mogwai/MogwaiScripting.cpp b/Source/Mogwai/MogwaiScripting.cpp new file mode 100644 index 000000000..f3b8f5537 --- /dev/null +++ b/Source/Mogwai/MogwaiScripting.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" + +namespace Mogwai +{ + namespace + { + const std::string kRunScript = "script"; + const std::string kLoadScene = "loadScene"; + const std::string kSaveConfig = "saveConfig"; + const std::string kAddGraph = "addGraph"; + const std::string kRemoveGraph = "removeGraph"; + const std::string kToggleUI = "ui"; + const std::string kResizeSwapChain = "resizeSwapChain"; + const std::string kActiveGraph = "activeGraph"; + const std::string kGetGraph = "graph"; + const std::string kGetScene = "scene"; + + template + std::string prepareHelpMessage(const T& g) + { + std::string s = Renderer::getVersionString() + "\nGlobal utility objects:\n"; + static const size_t kMaxSpace = 8; + for (auto n : g) + { + s += "\t`" + n.first + "`"; + s += (n.first.size() >= kMaxSpace) ? " " : std::string(kMaxSpace - n.first.size(), ' '); + s += n.second; + s += "\n"; + } + + s += "\nGlobal functions\n"; + s += "\trenderFrame() Render a frame. If the clock is not paused, it will advance by one tick. You can use it inside `For loops`, for example to loop over a specific time-range\n"; + s += "\texit() Exit Mogwai\n"; + return s; + } + + std::string windowConfig() + { + std::string s; + SampleConfig c = gpFramework->getConfig(); + s += "# Window Configuration\n"; + s += Scripting::makeMemberFunc(kRendererVar, kResizeSwapChain, c.windowDesc.width, c.windowDesc.height); + s += Scripting::makeMemberFunc(kRendererVar, kToggleUI, c.showUI); + return s; + } + } + + void Renderer::dumpConfig(std::string filename) const + { + if(filename.empty()) + { + if (!saveFileDialog(Scripting::kFileExtensionFilters, filename)) return; + } + + std::string s; + + if (mpScene) + { + s += "# Scene\n"; + s += Scripting::makeMemberFunc(kRendererVar, kLoadScene, filenameString(mpScene->getFilename())); + } + + if(mGraphs.size()) s += "\n# Graphs\n"; + for (auto& g : mGraphs) + { + s += RenderGraphExporter::getIR(g.pGraph); + s += std::string(kRendererVar) + "." + kAddGraph + "(" + RenderGraphIR::getFuncName(g.pGraph->getName()) + "())\n"; + } + + s += "\n" + windowConfig() + "\n"; + + for (auto& pe : mpExtensions) + { + auto eStr = pe->getScript(); + if (eStr.size()) s += eStr + "\n"; + } + + std::ofstream(filename) << s; + } + + void Renderer::registerScriptBindings(ScriptBindings::Module& m) + { + auto c = m.class_("Renderer"); + + c.func_(kRunScript.c_str(), &Renderer::loadScript, "filename"_a = std::string()); + c.func_(kLoadScene.c_str(), &Renderer::loadScene); + c.func_(kSaveConfig.c_str(), &Renderer::dumpConfig, "filename"_a = std::string()); + c.func_(kAddGraph.c_str(), &Renderer::addGraph); + c.func_(kRemoveGraph.c_str(), ScriptBindings::overload_cast(&Renderer::removeGraph)); + c.func_(kRemoveGraph.c_str(), ScriptBindings::overload_cast(&Renderer::removeGraph)); + c.func_(kGetGraph.c_str(), &Renderer::getGraph); + c.func_(kGetScene.c_str(), &Renderer::getScene); + + Extension::Bindings b(m, c); + b.addGlobalObject(kRendererVar, this, "The engine"); + for (auto& pe : mpExtensions) pe->scriptBindings(b); + mGlobalHelpMessage = prepareHelpMessage(b.mGlobalObjects); + + // Replace the `help` function + auto globalHelp = [this]() { pybind11::print(mGlobalHelpMessage);}; + m.func_("help", globalHelp); + + auto objectHelp = [](pybind11::object o) + { + auto b = pybind11::module::import("builtins"); + auto h = b.attr("help"); + h(o); + }; + m.func_("help", objectHelp); + + auto resize = [](Renderer* pRenderer, uint32_t width, uint32_t height) {gpFramework->resizeSwapChain(width, height); }; + c.func_(kResizeSwapChain.c_str(), resize); + + auto toggleUI = [](Renderer* pRenderer, bool show) {gpFramework->toggleUI(show); }; + c.func_(kToggleUI.c_str(), toggleUI, "show"_a = true); + c.func_(kActiveGraph.c_str(), &Renderer::getActiveGraph); + } +} diff --git a/Source/Mogwai/MogwaiSettings.cpp b/Source/Mogwai/MogwaiSettings.cpp new file mode 100644 index 000000000..331577dd5 --- /dev/null +++ b/Source/Mogwai/MogwaiSettings.cpp @@ -0,0 +1,354 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" +#include "MogwaiSettings.h" +#include +#include + +namespace Mogwai +{ + namespace + { + bool vsync = false; + constexpr char kTime[] = "t"; + constexpr char kExitTime[] = "exitTime"; + constexpr char kExitFrame[] = "exitFrame"; + + void shortcuts() + { + std::string s; + s += " 'F1' - Show the help message\n"; + s += " 'F10' - Show/Hide the FPS\n"; + s += " 'F11' - Toggle Main Menu Auto-Hide\n"; + s += "\n" + gpFramework->getKeyboardShortcutsStr(); + msgBox(s); + } + + void about() + { + std::string s = Renderer::getVersionString() + "\n"; + s += "Powered by Falcor "; + s += FALCOR_VERSION_STRING; + msgBox(s); + + } + + void showFps(Gui* pGui) + { + Gui::Window w(pGui, "##FPS", { 0, 0 }, { 10, 25 }, Gui::WindowFlags::AllowMove | Gui::WindowFlags::AutoResize | Gui::WindowFlags::SetFocus); + std::string msg = gpFramework->getFrameRate().getMsg(gpFramework->isVsyncEnabled()); + w.text(msg); + } + + void winSizeUI(Gui::Window& w) + { + static const uvec2 resolutions[] = + { + {1280, 720}, + {1920, 1080}, + {1920, 1200}, + {2560, 1440}, + {3840, 2160}, + }; + + constexpr uint32_t kCustomIndex = uint32_t(-1); + + static const auto initDropDown = [=](const uvec2 resolutions[], uint32_t count) -> Gui::DropdownList + { + Gui::DropdownList list; + for (uint32_t i = 0; i < count; i++) + { + list.push_back({ i, to_string(resolutions[i].x) + "x" + to_string(resolutions[i].y) }); + } + list.push_back({ kCustomIndex, "Custom" }); + return list; + }; + + auto initDropDownVal = [=](const uvec2 resolutions[], uint32_t count, uvec2 screenDims) + { + for (uint32_t i = 0; i < count; i++) + { + if (screenDims == resolutions[i]) return i; + } + return kCustomIndex; + }; + + uvec2 currentRes = gpFramework->getWindow()->getClientAreaSize(); + static const Gui::DropdownList dropdownList = initDropDown(resolutions, arraysize(resolutions)); + uint32_t currentVal = initDropDownVal(resolutions, arraysize(resolutions), currentRes); + w.text("Window Size"); + w.tooltip("The Window Size refers to the renderable area size (Swap-Chain dimensions)"); + + bool dropdownChanged = w.dropdown("##resdd", dropdownList, currentVal); + static uvec2 customSize; + static bool forceCustom = false; + + if (dropdownChanged) + { + if (currentVal == kCustomIndex) + { + forceCustom = true; + } + else + { + customSize = {}; + gpFramework->resizeSwapChain(resolutions[currentVal].x, resolutions[currentVal].y); + } + } + + if (currentVal == kCustomIndex || forceCustom) + { + if (customSize.x == 0) customSize = currentRes; + + w.var("##custres", customSize); + if (w.button("Apply##custres", true)) + { + gpFramework->resizeSwapChain(customSize.x, customSize.y); + forceCustom = false; + } + if (w.button("Cancel##custres", true)) + { + customSize = currentRes; + forceCustom = false; + } + } + } + } + + void MogwaiSettings::windowSettings(Gui* pGui) + { + Gui::Window w(pGui, "Window", mShowWinSize, { 0, 0 }, { 350, 300 }, Gui::WindowFlags::AllowMove | Gui::WindowFlags::AutoResize | Gui::WindowFlags::ShowTitleBar | Gui::WindowFlags::CloseButton); + winSizeUI(w); + } + + void MogwaiSettings::timeSettings(Gui* pGui) + { + Gui::Window w(pGui, "Time", mShowTime, { 0, 0 }, { 350, 25 }, Gui::WindowFlags::AllowMove | Gui::WindowFlags::AutoResize | Gui::WindowFlags::ShowTitleBar | Gui::WindowFlags::CloseButton); + + Clock& clock = gpFramework->getGlobalClock(); + clock.renderUI(w); + w.separator(2); + + if (mExitTime || mExitFrame) + { + std::stringstream s; + s << "Exiting in "; + if(mExitTime) s << std::fixed << std::setprecision(2) << (mExitTime - clock.now()) << " seconds"; + if(mExitFrame) s << (mExitFrame - clock.frame()) << " frames"; + w.text(s.str()); + } + } + + void MogwaiSettings::graphs(Gui* pGui) + { + if (!mShowGraphUI || mpRenderer->mGraphs.empty()) return; + + Gui::Window w(pGui, "Graphs", mShowGraphUI, { 300, 400 }, { 10, 80 }, Gui::WindowFlags::Default); + if (!mShowGraphUI) return; + + if (mpRenderer->mEditorProcess == 0) + { + Gui::DropdownList graphList; + for (size_t i = 0; i < mpRenderer->mGraphs.size(); i++) graphList.push_back({ (uint32_t)i, mpRenderer->mGraphs[i].pGraph->getName() }); + uint32_t activeGraph = mpRenderer->mActiveGraph; + if (w.dropdown("Active Graph", graphList, activeGraph)) + { + mpRenderer->setActiveGraph(activeGraph); + } + + if (w.button("Edit")) mpRenderer->openEditor(); + if (w.button("Remove", true)) + { + mpRenderer->removeActiveGraph(); + if (mpRenderer->mGraphs.empty()) return; + } + w.separator(); + } + + // Active graph output + mpRenderer->graphOutputsGui(w); // MOGWAI shouldn't be here + + // Graph UI + w.separator(); + Gui::Group graphGroup(pGui, (mpRenderer->mGraphs[mpRenderer->mActiveGraph].pGraph->getName() + "##Graph").c_str()); + mpRenderer->mGraphs[mpRenderer->mActiveGraph].pGraph->renderUI(graphGroup); + } + + void MogwaiSettings::mainMenu(Gui* pGui) + { + if (mAutoHideMenu && mMousePosition.y >= 20) return; + + auto m = Gui::MainMenu(pGui); + + { + auto file = m.dropdown("File"); + if (file.item("Load Script", "Ctrl+O")) mpRenderer->loadScriptDialog(); + if (file.item("Save Config")) mpRenderer->dumpConfig(); + if (file.item("Load Scene", "Ctrl+Shift+O")) mpRenderer->loadScene(); + file.separator(); + if (file.item("Reload Render-Passes", "F5")) RenderPassLibrary::instance().reloadLibraries(gpFramework->getRenderContext()); + } + + { + auto view = m.dropdown("View"); + view.item("Graph UI", mShowGraphUI, "F6"); + view.item("Auto Hide", mAutoHideMenu, "F11"); + view.item("FPS", mShowFps, "F10"); + view.item("Time", mShowTime, "F9"); + view.item("Window Size", mShowWinSize); + view.separator(); + view.item("Console", mShowConsole, "`"); + } + + { + auto help = m.dropdown("Help"); + if (help.item("Shortcuts")) shortcuts(); + if (help.item("About")) about(); + } + } + + void MogwaiSettings::renderUI(Gui* pGui__) + { + Gui* pGui = (Gui*)pGui__; + mainMenu(pGui); + graphs(pGui); + if (mShowFps) showFps(pGui); + if (mShowTime) timeSettings(pGui); + if (mShowWinSize) windowSettings(pGui); + if (mShowConsole) Console::render(pGui__); + } + + void MogwaiSettings::exitIfNeeded() + { + auto& clock = gpFramework->getGlobalClock(); + if (mExitTime && (clock.now() >= mExitTime)) postQuitMessage(0); + if (mExitFrame && (clock.frame() >= mExitFrame)) postQuitMessage(0); + } + + void MogwaiSettings::beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) + { + exitIfNeeded(); + } + + bool MogwaiSettings::mouseEvent(const MouseEvent& e) + { + if (e.type == MouseEvent::Type::Move) mMousePosition = e.screenPos; + return false; + } + + bool noMods(InputModifiers m) + { + return !(m.isAltDown || m.isCtrlDown || m.isShiftDown); + } + + bool MogwaiSettings::keyboardEvent(const KeyboardEvent& e) + { + if (e.type == KeyboardEvent::Type::KeyPressed) + { + if (e.mods.isAltDown) return false; + + // Regular keystrokes + if (noMods(e.mods)) + { + switch (e.key) + { + case KeyboardEvent::Key::F1: + shortcuts(); + break; + case KeyboardEvent::Key::F10: + mShowFps = !mShowFps; + break; + case KeyboardEvent::Key::F11: + mAutoHideMenu = !mAutoHideMenu; + break; + case KeyboardEvent::Key::F6: + mShowGraphUI = !mShowGraphUI; + break; + case KeyboardEvent::Key::F9: + mShowTime = !mShowTime; + break; + case KeyboardEvent::Key::GraveAccent: + mShowConsole = !mShowConsole; + break; + default: + return false; + } + return true; + } + else if (e.mods.isCtrlDown) + { + if (e.key == KeyboardEvent::Key::O) + { + e.mods.isShiftDown ? mpRenderer->loadScene() : mpRenderer->loadScriptDialog(); + return true; + } + else return false; + } + } + return false; + } + + MogwaiSettings::MogwaiSettings(Renderer* pRenderer) : mpRenderer(pRenderer) + { + } + + MogwaiSettings::UniquePtr MogwaiSettings::create(Renderer* pRenderer) + { + return UniquePtr(new MogwaiSettings(pRenderer)); + } + + void MogwaiSettings::scriptBindings(Bindings& bindings) + { + auto& m = bindings.getModule(); + + auto mm = pybind11::module::import("falcor"); + auto t = mm.attr("Clock").cast>(); + + bindings.addGlobalObject(kTime, &gpFramework->getGlobalClock(), "Time Utilities"); + + auto setExitTime = [this](Clock*, double exitTime) {mExitTime = exitTime; mExitFrame = 0; }; + t.def(kExitTime, setExitTime); + + auto setExitFrame = [this](Clock*, uint64_t exitFrame) {mExitFrame = exitFrame; mExitTime = 0; }; + t.def(kExitFrame, setExitFrame); + + auto showUI = [this](Clock*, bool show) { mShowTime = show; }; + t.def("ui", showUI ,"show"_a = true); + } + + std::string MogwaiSettings::getScript() + { + std::string s; + + s += "# Global Settings\n"; + s += gpFramework->getGlobalClock().getScript(kTime) + "\n"; + if(mExitTime) s += Scripting::makeMemberFunc(kTime, kExitTime, mExitTime); + if(mExitFrame) s += Scripting::makeMemberFunc(kTime, kExitFrame, mExitFrame); + return s; + } +} diff --git a/Framework/Source/API/ResourceViews.cpp b/Source/Mogwai/MogwaiSettings.h similarity index 59% rename from Framework/Source/API/ResourceViews.cpp rename to Source/Mogwai/MogwaiSettings.h index 279629b83..78ee956d9 100644 --- a/Framework/Source/API/ResourceViews.cpp +++ b/Source/Mogwai/MogwaiSettings.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,56 +25,44 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Framework.h" -#include "ResourceViews.h" +#pragma once +#include "Mogwai.h" -namespace Falcor +namespace Mogwai { - ResourceWeakPtr getEmptyTexture(); + class Renderer; - ShaderResourceView::SharedPtr ShaderResourceView::getNullView() + class MogwaiSettings : public Extension { - if (!gNullSrv) - { - gNullSrv = create(getEmptyTexture(), 0, 1, 0, 1); - } - return gNullSrv; - } + public: + using UniquePtr = std::unique_ptr; + static UniquePtr create(Renderer* pRenderer); - DepthStencilView::SharedPtr DepthStencilView::getNullView() - { - if (!gNullDsv) - { - gNullDsv = create(getEmptyTexture(), 0, 0, 1); - } - return gNullDsv; - } + void renderUI(Gui* pGui) override; + bool mouseEvent(const MouseEvent& e) override; + bool keyboardEvent(const KeyboardEvent& e) override; + void scriptBindings(Bindings& bindings) override; + std::string getScript() override; + void beginFrame(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - UnorderedAccessView::SharedPtr UnorderedAccessView::getNullView() - { - if (!gNullUav) - { - gNullUav = create(getEmptyTexture(), 0, 0, 1); - } - return gNullUav; - } + private: + MogwaiSettings(Renderer* pRenderer); + Renderer* mpRenderer; - RenderTargetView::SharedPtr RenderTargetView::getNullView() - { - if (!gNullRtv) - { - gNullRtv = create(getEmptyTexture(), 0, 0, 1); - } - return gNullRtv; - } + void mainMenu(Gui* pGui); + void graphs(Gui* pGui); + void timeSettings(Gui* pGui); + void windowSettings(Gui* pGui); + void exitIfNeeded(); - - ConstantBufferView::SharedPtr ConstantBufferView::getNullView() - { - if (!gNullCbv) - { - create(ResourceWeakPtr()); - } - return gNullCbv; - } + bool mAutoHideMenu = false; + bool mShowFps = true; + bool mShowGraphUI = true; + bool mShowConsole = false; + bool mShowTime = false; + bool mShowWinSize = false; + uvec2 mMousePosition; + double mExitTime = 0; + uint64_t mExitFrame = 0; + }; } diff --git a/Source/Mogwai/Testing/testCSM.py b/Source/Mogwai/Testing/testCSM.py new file mode 100644 index 000000000..d9ba00259 --- /dev/null +++ b/Source/Mogwai/Testing/testCSM.py @@ -0,0 +1,14 @@ +def render_graph_testCSM(): + csm = RenderGraph("Cascaded Shadow Maps") + csm.addPass(RenderPass("DepthPass"), "DepthPrePass") + csm.addPass(RenderPass("CascadedShadowMaps"), "ShadowPass") + + csm.addEdge("DepthPrePass.depth", "ShadowPass.depth"); + + csm.markOutput("ShadowPass.visibility") + + return csm + +csm = render_graph_testCSM() +try: m.addGraph(csm) +except NameError: None diff --git a/Source/Mogwai/Testing/testFXAA.py b/Source/Mogwai/Testing/testFXAA.py new file mode 100644 index 000000000..d5d6ada11 --- /dev/null +++ b/Source/Mogwai/Testing/testFXAA.py @@ -0,0 +1,20 @@ +def render_graph_testFXAA(): + testFXAA = RenderGraph("ForwardRenderer") + DepthPass = RenderPass("DepthPass", {'depthFormat': ResourceFormat.D32Float}) + testFXAA.addPass(DepthPass, "DepthPass") + SkyBox = RenderPass("SkyBox") + testFXAA.addPass(SkyBox, "SkyBox") + ForwardLightingPass = RenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) + testFXAA.addPass(ForwardLightingPass, "ForwardLightingPass") + FXAAPass = RenderPass("FXAAPass") + testFXAA.addPass(FXAAPass, "FXAA") + testFXAA.addEdge("DepthPass.depth", "ForwardLightingPass.depth") + testFXAA.addEdge("DepthPass.depth", "SkyBox.depth") + testFXAA.addEdge("SkyBox.target", "ForwardLightingPass.color") + testFXAA.addEdge("ForwardLightingPass.color", "FXAA.src") + testFXAA.markOutput("FXAA.dst") + return testFXAA + +test_FXAA = render_graph_testFXAA() +try: m.addGraph(test_FXAA) +except NameError: None diff --git a/Source/Mogwai/Testing/testForwardRendering.py b/Source/Mogwai/Testing/testForwardRendering.py new file mode 100644 index 000000000..a19f7163c --- /dev/null +++ b/Source/Mogwai/Testing/testForwardRendering.py @@ -0,0 +1,21 @@ +def render_graph_testForwardRendering(): + testForwardRendering = RenderGraph("ForwardRenderer") + DepthPass = RenderPass("DepthPass", {'depthFormat': ResourceFormat.D32Float}) + testForwardRendering.addPass(DepthPass, "DepthPass") + SkyBox = RenderPass("SkyBox") + testForwardRendering.addPass(SkyBox, "SkyBox") + ForwardLightingPass = RenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) + testForwardRendering.addPass(ForwardLightingPass, "ForwardLightingPass") + BlitPass = RenderPass("BlitPass", {'filter': SamplerFilter.Linear}) + testForwardRendering.addPass(BlitPass, "BlitPass") + testForwardRendering.addEdge("ForwardLightingPass.color", "BlitPass.src") + testForwardRendering.addEdge("DepthPass.depth", "ForwardLightingPass.depth") + testForwardRendering.addEdge("DepthPass.depth", "SkyBox.depth") + testForwardRendering.addEdge("SkyBox.target", "ForwardLightingPass.color") + testForwardRendering.markOutput("BlitPass.dst") + testForwardRendering.markOutput("ForwardLightingPass.motionVecs") + return testForwardRendering + +testForwardRendering = render_graph_testForwardRendering() +try: m.addGraph(testForwardRendering) +except NameError: None diff --git a/Source/Mogwai/Testing/testGaussianBlur.py b/Source/Mogwai/Testing/testGaussianBlur.py new file mode 100644 index 000000000..03121d39a --- /dev/null +++ b/Source/Mogwai/Testing/testGaussianBlur.py @@ -0,0 +1,20 @@ +def render_graph_testGaussianBlur(): + testGaussianBlur = RenderGraph("Gaussian Blur") + DepthPass = RenderPass("DepthPass", {'depthFormat': ResourceFormat.D32Float}) + testGaussianBlur.addPass(DepthPass, "DepthPass") + SkyBox = RenderPass("SkyBox") + testGaussianBlur.addPass(SkyBox, "SkyBox") + ForwardLightingPass = RenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) + testGaussianBlur.addPass(ForwardLightingPass, "ForwardLightingPass") + GaussianBlurPass = RenderPass("GaussianBlurPass") + testGaussianBlur.addPass(GaussianBlurPass, "GaussianBlur") + testGaussianBlur.addEdge("DepthPass.depth", "ForwardLightingPass.depth") + testGaussianBlur.addEdge("DepthPass.depth", "SkyBox.depth") + testGaussianBlur.addEdge("SkyBox.target", "ForwardLightingPass.color") + testGaussianBlur.addEdge("ForwardLightingPass.color", "GaussianBlur.src") + testGaussianBlur.markOutput("GaussianBlur.dst") + return testGaussianBlur + +test_Gaussian_Blur = render_graph_testGaussianBlur() +try: m.addGraph(test_Gaussian_Blur) +except NameError: None diff --git a/Source/Mogwai/Testing/testSSAO.py b/Source/Mogwai/Testing/testSSAO.py new file mode 100644 index 000000000..dd5698c2d --- /dev/null +++ b/Source/Mogwai/Testing/testSSAO.py @@ -0,0 +1,21 @@ +def render_graph_testSSAO(): + testSSAO = RenderGraph("ForwardRenderer") + DepthPass = RenderPass("DepthPass", {'depthFormat': ResourceFormat.D32Float}) + testSSAO.addPass(DepthPass, "DepthPass") + SkyBox = RenderPass("SkyBox") + testSSAO.addPass(SkyBox, "SkyBox") + ForwardLightingPass = RenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) + testSSAO.addPass(ForwardLightingPass, "ForwardLightingPass") + SSAOPass = RenderPass("SSAOPass") + testSSAO.addPass(SSAOPass, "SSAO") + testSSAO.addEdge("DepthPass.depth", "ForwardLightingPass.depth") + testSSAO.addEdge("DepthPass.depth", "SkyBox.depth") + testSSAO.addEdge("SkyBox.target", "ForwardLightingPass.color") + testSSAO.addEdge("DepthPass.depth", "SSAO.depth") + testSSAO.addEdge("ForwardLightingPass.color", "SSAO.colorIn") + testSSAO.markOutput("SSAO.colorOut") + return testSSAO + +test_SSAO = render_graph_testSSAO() +try: m.addGraph(test_SSAO) +except NameError: None diff --git a/Source/Mogwai/Testing/testSVGF.py b/Source/Mogwai/Testing/testSVGF.py new file mode 100644 index 000000000..6286435aa --- /dev/null +++ b/Source/Mogwai/Testing/testSVGF.py @@ -0,0 +1,57 @@ +def render_graph_DefaultRenderGraph(): + g = RenderGraph("DefaultRenderGraph") + loadRenderPassLibrary("AccumulatePass.dll") + loadRenderPassLibrary("MinimalPathTracer.dll") + loadRenderPassLibrary("ErrorMeasurePass.dll") + loadRenderPassLibrary("PixelInspectorPass.dll") + loadRenderPassLibrary("DebugPasses.dll") + loadRenderPassLibrary("GBuffer.dll") + loadRenderPassLibrary("SamplePassLibrary.dll") + loadRenderPassLibrary("PassLibraryTemplate.dll") + loadRenderPassLibrary("PathTracer.dll") + loadRenderPassLibrary("SVGFPass.dll") + loadRenderPassLibrary("TemporalDelayPass.dll") + SVGFPass = RenderPass("SVGFPass", {'Enabled': True, 'Iterations': 4, 'FeedbackTap': 1, 'VarianceEpsilon': 9.999999747378752e-05, 'PhiColor': 10.0, 'PhiNormal': 128.0, 'Alpha': 0.05000000074505806, 'MomentsAlpha': 0.20000000298023224}) + g.addPass(SVGFPass, "SVGFPass") + GBufferRaster = RenderPass("GBufferRaster", {'cull': CullMode.CullBack}) + g.addPass(GBufferRaster, "GBufferRaster") + #PathTracer = RenderPass("PathTracer", {'mSharedParams': PathTracerParams(thresholdDirect=10.000000, samplesPerPixel=1, useAnalyticLights=1, thresholdIndirect=10.000000, maxBounces=3, forceAlphaOne=1, clampDirect=0, useEmissiveLights=1, clampIndirect=0, useEnvLight=1, useBRDFSampling=1, useEnvBackground=1, useMIS=1, misHeuristic=1, misPowerExponent=2.000000, useEmissiveLightSampling=1, probabilityAbsorption=0.200000, useRussianRoulette=0, useFixedSeed=0), 'mSelectedSampleGenerator': 0, 'mSelectedEmissiveSampler': EmissiveLightSamplerType.LightBVH}) + PathTracer = RenderPass("MegakernelPathTracer", {'mSharedParams': PathTracerParams(thresholdDirect=10.000000, samplesPerPixel=1, useAnalyticLights=1, thresholdIndirect=10.000000, maxBounces=3, forceAlphaOne=1, clampDirect=0, useEmissiveLights=1, clampIndirect=0, useEnvLight=1, useBRDFSampling=1, useEnvBackground=1, useMIS=1, misHeuristic=1, misPowerExponent=2.000000, useEmissiveLightSampling=1, probabilityAbsorption=0.200000, useRussianRoulette=0, useFixedSeed=0), 'mSelectedSampleGenerator': 0, 'mSelectedEmissiveSampler': EmissiveLightSamplerType.Uniform}) + g.addPass(PathTracer, "PathTracer") + g.addEdge("PathTracer.color", "SVGFPass.Color") + g.addEdge("GBufferRaster.posW", "PathTracer.posW") + g.addEdge("PathTracer.albedo", "SVGFPass.Albedo") + g.addEdge("GBufferRaster.normW", "PathTracer.normalW") + g.addEdge("GBufferRaster.bitangentW", "PathTracer.bitangentW") + g.addEdge("GBufferRaster.faceNormalW", "PathTracer.faceNormalW") + g.addEdge("GBufferRaster.diffuseOpacity", "PathTracer.mtlDiffOpacity") + g.addEdge("GBufferRaster.specRough", "PathTracer.mtlSpecRough") + g.addEdge("GBufferRaster.emissive", "PathTracer.mtlEmissive") + g.addEdge("GBufferRaster.matlExtra", "PathTracer.mtlParams") + g.addEdge("GBufferRaster.emissive", "SVGFPass.Emission") + g.addEdge("GBufferRaster.posW", "SVGFPass.WorldPosition") + g.addEdge("GBufferRaster.normW", "SVGFPass.WorldNormal") + g.addEdge("GBufferRaster.pnFwidth", "SVGFPass.PositionNormalFwidth") + g.addEdge("GBufferRaster.linearZ", "SVGFPass.LinearZ") + g.addEdge("GBufferRaster.mvec", "SVGFPass.MotionVec") + g.markOutput("SVGFPass.Filtered image") + g.markOutput("GBufferRaster.mvec") + g.markOutput("PathTracer.color") + g.markOutput("GBufferRaster.posW") + g.markOutput("PathTracer.albedo") + g.markOutput("GBufferRaster.normW") + g.markOutput("GBufferRaster.bitangentW") + g.markOutput("GBufferRaster.diffuseOpacity") + g.markOutput("GBufferRaster.specRough") + g.markOutput("GBufferRaster.emissive") + g.markOutput("GBufferRaster.matlExtra") + g.markOutput("GBufferRaster.emissive") + g.markOutput("GBufferRaster.posW") + g.markOutput("GBufferRaster.normW") + g.markOutput("GBufferRaster.pnFwidth") + g.markOutput("GBufferRaster.linearZ") + return g + +DefaultRenderGraph = render_graph_DefaultRenderGraph() +try: m.addGraph(DefaultRenderGraph) +except NameError: None diff --git a/Source/Mogwai/Testing/testTAA.py b/Source/Mogwai/Testing/testTAA.py new file mode 100644 index 000000000..fb25c5118 --- /dev/null +++ b/Source/Mogwai/Testing/testTAA.py @@ -0,0 +1,21 @@ +def render_graph_testTAA(): + testTAA = RenderGraph("ForwardRenderer") + DepthPass = RenderPass("DepthPass", {'depthFormat': ResourceFormat.D32Float}) + testTAA.addPass(DepthPass, "DepthPass") + SkyBox = RenderPass("SkyBox") + testTAA.addPass(SkyBox, "SkyBox") + ForwardLightingPass = RenderPass("ForwardLightingPass", {'sampleCount': 1, 'enableSuperSampling': False}) + testTAA.addPass(ForwardLightingPass, "ForwardLightingPass") + TAAPass = RenderPass("TemporalAAPass") + testTAA.addPass(TAAPass, "TAA") + testTAA.addEdge("DepthPass.depth", "ForwardLightingPass.depth") + testTAA.addEdge("DepthPass.depth", "SkyBox.depth") + testTAA.addEdge("SkyBox.target", "ForwardLightingPass.color") + testTAA.addEdge("ForwardLightingPass.color", "TAA.colorIn") + testTAA.addEdge("ForwardLightingPass.motionVecs", "TAA.motionVecs") + testTAA.markOutput("TAA.colorOut") + return testTAA + +test_TAA = render_graph_testTAA() +try: m.addGraph(test_TAA) +except NameError: None diff --git a/Source/Mogwai/Testing/testToneMapping.py b/Source/Mogwai/Testing/testToneMapping.py new file mode 100644 index 000000000..d02ffd0d3 --- /dev/null +++ b/Source/Mogwai/Testing/testToneMapping.py @@ -0,0 +1,16 @@ +def render_graph_testToneMapping(): + testToneMapping = RenderGraph("ToneMapper") + ImageLoader = RenderPass("ImageLoader", {'fileName': '', 'mips': False, 'srgb': True, 'filename' : "StockImage.jpg"}) + testToneMapping.addPass(ImageLoader, "ImageLoader") + ToneMapping = RenderPass("ToneMappingPass", {'operator': ToneMapOp.Aces}) + testToneMapping.addPass(ToneMapping, "ToneMapping") + BlitPass = RenderPass("BlitPass", {'filter': SamplerFilter.Linear}) + testToneMapping.addPass(BlitPass, "BlitPass") + testToneMapping.addEdge("ImageLoader.dst", "ToneMapping.src") + testToneMapping.addEdge("ToneMapping.dst", "BlitPass.src") + testToneMapping.markOutput("BlitPass.dst") + return testToneMapping + +testToneMapping = render_graph_testToneMapping() +try: m.addGraph(testToneMapping) +except NameError: None diff --git a/Source/Mogwai/stdafx.cpp b/Source/Mogwai/stdafx.cpp new file mode 100644 index 000000000..d229d051f --- /dev/null +++ b/Source/Mogwai/stdafx.cpp @@ -0,0 +1,28 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "stdafx.h" diff --git a/Source/Mogwai/stdafx.h b/Source/Mogwai/stdafx.h new file mode 100644 index 000000000..655690a34 --- /dev/null +++ b/Source/Mogwai/stdafx.h @@ -0,0 +1,30 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "Mogwai.h" diff --git a/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang b/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang new file mode 100644 index 000000000..69831045f --- /dev/null +++ b/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang @@ -0,0 +1,125 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Temporal accumulation render pass. + + There are entry points for each of the three supported accumulation modes. + Note that for the compensated summation mode the shader _must_ be compiled + for precise floating-point operations (no reordering). + + In all modes, the shader writes the current accumulated average to the + output texture. The intermediate buffers are internal to the pass. +*/ + +cbuffer PerFrameCB +{ + uint2 gResolution; + uint gAccumCount; +} + +// Input data to accumulate and accumulated output. +Texture2D gCurFrame; +RWTexture2D gOutputFrame; + +// Last frame data, format depends on accumulation mode. +RWTexture2D gLastFrameSum; // If mode is Single or SingleKahan +RWTexture2D gLastFrameCorr; // If mode is SingleKahan +RWTexture2D gLastFrameSumLo; // If mode is Double +RWTexture2D gLastFrameSumHi; // If mode is Double + + +/** Single precision standard summation. +*/ +[numthreads(16, 16, 1)] +void accumulateSingle(uint3 dispatchThreadId : SV_DispatchThreadID) +{ + if (any(dispatchThreadId.xy >= gResolution)) return; + const uint2 pixelPos = dispatchThreadId.xy; + const float4 curColor = gCurFrame[pixelPos]; + + // Fetch previous sum and compute the new sum. + float4 sum = gLastFrameSum[pixelPos] + curColor; + float4 output = sum / (gAccumCount + 1); + + gLastFrameSum[pixelPos] = sum; + gOutputFrame[pixelPos] = output; +} + +/** Single precision compensated summation. +*/ +[numthreads(16, 16, 1)] +void accumulateSingleCompensated(uint3 dispatchThreadId : SV_DispatchThreadID) +{ + if (any(dispatchThreadId.xy >= gResolution)) return; + const uint2 pixelPos = dispatchThreadId.xy; + const float4 curColor = gCurFrame[pixelPos]; + + // Fetch the previous sum and running compensation term. + float4 sum = gLastFrameSum[pixelPos]; + float4 c = gLastFrameCorr[pixelPos]; // c measures how large (+) or small (-) the current sum is compared to what it should be. + + // Adjust current value to minimize the running error. + // Compute the new sum by adding the adjusted current value. + float4 y = curColor - c; + float4 sumNext = sum + y; // The value we'll see in 'sum' on the next iteration. + float4 output = sumNext / (gAccumCount + 1); + + gLastFrameSum[pixelPos] = sumNext; + gLastFrameCorr[pixelPos] = (sumNext - sum) - y; // Store new correction term. + gOutputFrame[pixelPos] = output; +} + +/** Double precision standard summation. +*/ +[numthreads(16, 16, 1)] +void accumulateDouble(uint3 dispatchThreadId : SV_DispatchThreadID) +{ + if (any(dispatchThreadId.xy >= gResolution)) return; + const uint2 pixelPos = dispatchThreadId.xy; + const float4 curColor = gCurFrame[pixelPos]; + + // Fetch the previous sum in double precision. + // There is no 'double' resource format, so the bits are stored in two uint4 textures. + uint4 sumLo = gLastFrameSumLo[pixelPos]; + uint4 sumHi = gLastFrameSumHi[pixelPos]; + + double sum[4]; + float4 output; + + for (int i = 0; i < 4; i++) + { + sum[i] = asdouble(sumLo[i], sumHi[i]); + sum[i] += (double)curColor[i]; + asuint(sum[i], sumLo[i], sumHi[i]); + output[i] = (float)(sum[i] / (double)(gAccumCount + 1)); + } + + gLastFrameSumLo[pixelPos] = sumLo; + gLastFrameSumHi[pixelPos] = sumHi; + gOutputFrame[pixelPos] = output; +} diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp new file mode 100644 index 000000000..17738cde5 --- /dev/null +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp @@ -0,0 +1,248 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "AccumulatePass.h" + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +static void regAccumulatePass(ScriptBindings::Module& m) +{ + auto e = m.enum_("AccumulatePrecision"); + e.regEnumVal(AccumulatePass::Precision::Double); + e.regEnumVal(AccumulatePass::Precision::Single); + e.regEnumVal(AccumulatePass::Precision::SingleCompensated); +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("AccumulatePass", "Temporal accumulation", AccumulatePass::create); + ScriptBindings::registerBinding(regAccumulatePass); +} + +namespace +{ + const char kShaderFile[] = "RenderPasses/AccumulatePass/Accumulate.cs.slang"; + + const char kInputChannel[] = "input"; + const char kOutputChannel[] = "output"; + + // Serialized parameters + const char kEnableAccumulation[] = "enableAccumulation"; + const char kPrecisionMode[] = "precisionMode"; + + const Gui::DropdownList kModeSelectorList = + { + { (uint32_t)AccumulatePass::Precision::Double, "Double precision" }, + { (uint32_t)AccumulatePass::Precision::Single, "Single precision" }, + { (uint32_t)AccumulatePass::Precision::SingleCompensated, "Single precision (compensated)" }, + }; +} + +AccumulatePass::SharedPtr AccumulatePass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new AccumulatePass); + return pPass->init(dict) ? pPass : nullptr; +} + +bool AccumulatePass::init(const Dictionary& dict) +{ + // Deserialize pass from dictionary. + for (const auto& v : dict) + { + if (v.key() == kEnableAccumulation) mEnableAccumulation = v.val(); + else if (v.key() == kPrecisionMode) mPrecisionMode = v.val(); + else logWarning("Unknown field `" + v.key() + "` in AccumulatePass dictionary"); + } + + // Create accumulation programs. + // Note only compensated summation needs precise floating-point mode. + if (!(mpProgram[Precision::Double] = ComputeProgram::createFromFile(kShaderFile, "accumulateDouble", Program::DefineList(), Shader::CompilerFlags::TreatWarningsAsErrors))) return false; + if (!(mpProgram[Precision::Single] = ComputeProgram::createFromFile(kShaderFile, "accumulateSingle", Program::DefineList(), Shader::CompilerFlags::TreatWarningsAsErrors))) return false; + if (!(mpProgram[Precision::SingleCompensated] = ComputeProgram::createFromFile(kShaderFile, "accumulateSingleCompensated", Program::DefineList(), Shader::CompilerFlags::FloatingPointModePrecise | Shader::CompilerFlags::TreatWarningsAsErrors))) return false; + if (!(mpVars = ComputeVars::create(mpProgram[Precision::Single]->getReflector()))) return false; + + mpState = ComputeState::create(); + + return true; +} + +Dictionary AccumulatePass::getScriptingDictionary() +{ + Dictionary dict; + dict[kEnableAccumulation] = mEnableAccumulation; + dict[kPrecisionMode] = mPrecisionMode; + return dict; +} + +RenderPassReflection AccumulatePass::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + reflector.addInput(kInputChannel, "Input data to be temporally accumulated").bindFlags(ResourceBindFlags::ShaderResource); + reflector.addOutput(kOutputChannel, "Output data that is temporally accumulated").bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource).format(ResourceFormat::RGBA32Float); + // TODO: ResourceBindFlags::ShaderResource from output resource + + return reflector; +} + +void AccumulatePass::compile(RenderContext* pContext, const CompileData& compileData) +{ + // Reset accumulation when resolution changes. + if (compileData.defaultTexDims != mFrameDim) + { + mFrameCount = 0; + mFrameDim = compileData.defaultTexDims; + } +} + +void AccumulatePass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Query refresh flags passed down from the application and other passes. + auto& dict = renderData.getDictionary(); + RenderPassRefreshFlags refreshFlags = (RenderPassRefreshFlags)(dict.keyExists(kRenderPassRefreshFlags) ? dict[kRenderPassRefreshFlags] : 0u); + + // If any refresh flag is set, we reset frame accumulation. + if (refreshFlags != RenderPassRefreshFlags::None) mFrameCount = 0; + + // Reset accumulation upon all scene changes, except camera jitter and history changes. + // TODO: Add UI options to select which changes should trigger reset + if (mpScene) + { + auto sceneUpdates = mpScene->getUpdates(); + if ((sceneUpdates & ~Scene::UpdateFlags::CameraPropertiesChanged) != Scene::UpdateFlags::None) + { + mFrameCount = 0; + } + if (is_set(sceneUpdates, Scene::UpdateFlags::CameraPropertiesChanged)) + { + auto excluded = Camera::Changes::Jitter | Camera::Changes::History; + auto cameraChanges = mpScene->getCamera()->getChanges(); + if ((cameraChanges & ~excluded) != Camera::Changes::None) mFrameCount = 0; + } + } + + // Grab our input/output buffers. + Texture::SharedPtr pSrc = renderData[kInputChannel]->asTexture(); + Texture::SharedPtr pDst = renderData[kOutputChannel]->asTexture(); + + assert(pSrc && pDst); + assert(pSrc->getWidth() == pDst->getWidth() && pSrc->getHeight() == pDst->getHeight()); + const glm::uvec2 resolution = glm::uvec2(pSrc->getWidth(), pSrc->getHeight()); + + // If accumulation is disabled, just blit the source to the destination and return. + if (!mEnableAccumulation) + { + // Only blit mip 0 and array slice 0, because that's what the accumulation uses otherwise otherwise. + pRenderContext->blit(pSrc->getSRV(0, 1, 0, 1), pDst->getRTV(0, 0, 1)); + return; + } + + // Setup accumulation. + prepareAccumulation(pRenderContext, resolution.x, resolution.y); + + // Set shader parameters. + mpVars["PerFrameCB"]["gResolution"] = resolution; + mpVars["PerFrameCB"]["gAccumCount"] = mFrameCount++; + mpVars["gCurFrame"] = pSrc; + mpVars["gOutputFrame"] = pDst; + + // Bind accumulation buffers. Some of these may be nullptr's. + mpVars["gLastFrameSum"] = mpLastFrameSum; + mpVars["gLastFrameCorr"] = mpLastFrameCorr; + mpVars["gLastFrameSumLo"] = mpLastFrameSumLo; + mpVars["gLastFrameSumHi"] = mpLastFrameSumHi; + + // Run the accumulation program. + auto pProgram = mpProgram[mPrecisionMode]; + assert(pProgram); + glm::uvec3 numGroups = div_round_up(glm::uvec3(resolution.x, resolution.y, 1u), pProgram->getReflector()->getThreadGroupSize()); + mpState->setProgram(pProgram); + pRenderContext->dispatch(mpState.get(), mpVars.get(), numGroups); +} + +void AccumulatePass::renderUI(Gui::Widgets& widget) +{ + if (widget.checkbox("Accumulate temporally", mEnableAccumulation)) + { + // Reset accumulation when it is toggled. + mFrameCount = 0; + } + + if (mEnableAccumulation) + { + if (widget.dropdown("Mode", kModeSelectorList, (uint32_t&)mPrecisionMode)) + { + // Reset accumulation when mode changes. + mFrameCount = 0; + } + + const std::string text = std::string("Frames accumulated ") + std::to_string(mFrameCount); + widget.text(text.c_str()); + } +} + +void AccumulatePass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + // Reset accumulation when the scene changes. + mFrameCount = 0; + mpScene = pScene; +} + +void AccumulatePass::prepareAccumulation(RenderContext* pRenderContext, uint32_t width, uint32_t height) +{ + // Allocate/resize/clear buffers for intermedate data. These are different depending on accumulation mode. + // Buffers that are not used in the current mode are released. + auto prepareBuffer = [&](Texture::SharedPtr& pBuf, ResourceFormat format, bool bufUsed) + { + if (!bufUsed) + { + pBuf = nullptr; + return; + } + // (Re-)create buffer if needed. + if (!pBuf || pBuf->getWidth() != width || pBuf->getHeight() != height) + { + pBuf = Texture::create2D(width, height, format, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + assert(pBuf); + mFrameCount = 0; + } + // Clear data if accumulation has been reset (either above or somewhere else). + if (mFrameCount == 0) + { + if (getFormatType(format) == FormatType::Float) pRenderContext->clearUAV(pBuf->getUAV().get(), float4(0.f)); + else pRenderContext->clearUAV(pBuf->getUAV().get(), glm::uvec4(0)); + } + }; + + prepareBuffer(mpLastFrameSum, ResourceFormat::RGBA32Float, mPrecisionMode == Precision::Single || mPrecisionMode == Precision::SingleCompensated); + prepareBuffer(mpLastFrameCorr, ResourceFormat::RGBA32Float, mPrecisionMode == Precision::SingleCompensated); + prepareBuffer(mpLastFrameSumLo, ResourceFormat::RGBA32Uint, mPrecisionMode == Precision::Double); + prepareBuffer(mpLastFrameSumHi, ResourceFormat::RGBA32Uint, mPrecisionMode == Precision::Double); +} diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.h b/Source/RenderPasses/AccumulatePass/AccumulatePass.h new file mode 100644 index 000000000..54dae9a8d --- /dev/null +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.h @@ -0,0 +1,103 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" + +using namespace Falcor; + +/** Temporal accumulation render pass. + + This pass takes a texture as input and writes the temporally accumulated + result to an output texture. The pass keeps intermediate data internally. + + For accumulating many samples for ground truth rendering etc., fp32 precision + is not always sufficient. The pass supports higher precision modes using + either error compensation (Kahan summation) or double precision math. +*/ +class AccumulatePass : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + virtual ~AccumulatePass() = default; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return "Temporal accumulation pass"; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + + enum class Precision : uint32_t + { + Double, ///< Standard summation in double precision. + Single, ///< Standard summation in single precision. + SingleCompensated, ///< Compensated summation (Kahan summation) in single precision. + }; + +protected: + AccumulatePass() = default; + bool init(const Dictionary& dict); + void prepareAccumulation(RenderContext* pRenderContext, uint32_t width, uint32_t height); + + // Internal state + Scene::SharedPtr mpScene; ///< The current scene (or nullptr if no scene). + std::map mpProgram; ///< Accumulation programs, one per mode. + ComputeVars::SharedPtr mpVars; ///< Program variables. + ComputeState::SharedPtr mpState; + + uint32_t mFrameCount = 0; ///< Number of accumulated frames. This is reset upon changes. + glm::uvec2 mFrameDim = { 0, 0 }; ///< Current frame dimension in pixels. + Texture::SharedPtr mpLastFrameSum; ///< Last frame running sum. Used in Single and SingleKahan mode. + Texture::SharedPtr mpLastFrameCorr; ///< Last frame running compensation term. Used in SingleKahan mode. + Texture::SharedPtr mpLastFrameSumLo; ///< Last frame running sum (lo bits). Used in Double mode. + Texture::SharedPtr mpLastFrameSumHi; ///< Last frame running sum (hi bits). Used in Double mode. + + // UI variables + bool mEnableAccumulation = true; ///< UI control if accumulation is enabled. + Precision mPrecisionMode = Precision::Single; +}; + +#define enum2str(a) case AccumulatePass::Precision::a: return #a +inline std::string to_string(AccumulatePass::Precision mode) +{ + switch (mode) + { + enum2str(Double); + enum2str(Single); + enum2str(SingleCompensated); + default: + should_not_get_here(); + return ""; + } +} +#undef enum2str diff --git a/Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj b/Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj similarity index 69% rename from Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj rename to Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj index 00ecedd06..407009ca2 100644 --- a/Samples/Core/ShaderBuffers/ShaderBuffers.vcxproj +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj @@ -1,4 +1,4 @@ - + @@ -10,54 +10,52 @@ x64 + + {081FD8DE-6C92-4CDC-84AD-C514F7E83F93} + Win32Proj + AccumulatePass + 10.0.17763.0 + AccumulatePass + + + + + + + + - + - + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} - - - - - Document - + - - {E9189681-F552-4811-9B9C-C88E63D21363} - Win32Proj - ShaderBuffers - 10.0.17763.0 - - - Application + DynamicLibrary true v141 Unicode + Data\RenderPasses\$(ProjectName) - Application + DynamicLibrary false v141 true Unicode + Data\RenderPasses\$(ProjectName) - - - - - - true @@ -71,7 +69,10 @@ Level3 Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows @@ -86,7 +87,10 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows diff --git a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.filters b/Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj.filters similarity index 55% rename from Samples/Core/MultiPassPostProcess/MultiPassPostProcess.filters rename to Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj.filters index f6a8e06ff..b3bc6462f 100644 --- a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.filters +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.vcxproj.filters @@ -1,12 +1,12 @@  - + - + - + \ No newline at end of file diff --git a/Source/RenderPasses/AccumulatePass/x64/Debug/AccumulatePass.log b/Source/RenderPasses/AccumulatePass/x64/Debug/AccumulatePass.log new file mode 100644 index 000000000..98965997e --- /dev/null +++ b/Source/RenderPasses/AccumulatePass/x64/Debug/AccumulatePass.log @@ -0,0 +1 @@ +C:\Work\GIT\Falcor\Source\RenderPasses\AccumulatePass\AccumulatePass.vcxproj(22,5): error MSB4019: The imported project "C:\Work\GIT\Falcor\Falcor\Falcor.props" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk. diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp new file mode 100644 index 000000000..3b916d082 --- /dev/null +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp @@ -0,0 +1,426 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "BSDFViewer.h" + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("BSDFViewer", BSDFViewer::sDesc, BSDFViewer::create); +} + +namespace +{ + const char kFileViewerPass[] = "RenderPasses/BSDFViewer/BSDFViewer.cs.slang"; + const char kOutput[] = "output"; +} + +const char* BSDFViewer::sDesc = "BSDF Viewer"; + +BSDFViewer::SharedPtr BSDFViewer::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new BSDFViewer); + return pPass->init(dict) ? pPass : nullptr; +} + +bool BSDFViewer::init(const Dictionary& dict) +{ + // Defines to disable discard and gradient operations in Falcor's material system. + Program::DefineList defines = + { + {"_MS_DISABLE_ALPHA_TEST", ""}, + {"_DEFAULT_ALPHA_TEST", ""}, + }; + + // Create programs. + Program::Desc desc; + desc.addShaderLibrary(kFileViewerPass).csEntry("main").setShaderModel("6_0"); + mpViewerPass = ComputePass::create(desc, defines, false); + if (!mpViewerPass) return false; + + // Create a high-quality pseudorandom number generator. + mpSampleGenerator = SampleGenerator::create(SAMPLE_GENERATOR_UNIFORM); + if (!mpSampleGenerator) return false; + mpSampleGenerator->prepareProgram(mpViewerPass->getProgram().get()); + mpViewerPass->setVars(nullptr); // Trigger vars creation + + // Create readback buffer. + mPixelDataBuffer = StructuredBuffer::create(mpViewerPass->getProgram().get(), "gPixelData", 1u, ResourceBindFlags::UnorderedAccess); + if (!mPixelDataBuffer) return false; + + return true; +} + +Dictionary BSDFViewer::getScriptingDictionary() +{ + return Dictionary(); +} + +RenderPassReflection BSDFViewer::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + r.addOutput(kOutput, "Output buffer").format(ResourceFormat::RGBA32Float).bindFlags(ResourceBindFlags::UnorderedAccess); + return r; +} + +void BSDFViewer::compile(RenderContext* pContext, const CompileData& compileData) +{ + mParams.frameDim = compileData.defaultTexDims; + + // Place a square viewport centered in the frame. + uint32_t extent = std::min(mParams.frameDim.x, mParams.frameDim.y); + uint32_t xOffset = (mParams.frameDim.x - extent) / 2; + uint32_t yOffset = (mParams.frameDim.y - extent) / 2; + + mParams.viewportOffset = float2(xOffset, yOffset); + mParams.viewportScale = float2(1.f / extent); +} + +void BSDFViewer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + mpScene = pScene; + mpEnvProbe = nullptr; + mEnvProbeFilename = ""; + mMaterialList.clear(); + mParams.materialID = 0; + + if (pScene == nullptr) + { + mParams.useSceneMaterial = false; + mParams.useEnvMap = false; + } + else + { + // Bind the scene to our program. + Shader::DefineList defines = mpScene->getSceneDefines(); + mpViewerPass->getProgram()->addDefines(defines); + mpViewerPass->setVars(nullptr); // Trigger vars creation + mpViewerPass["gScene"] = mpScene->getParameterBlock(); + + // Load and bind environment map. + // We're getting the file name from the scene's LightProbe because that was used in the fscene files. + // TODO: Switch to use Scene::getEnvironmentMap() when the assets have been updated. + auto pLightProbe = mpScene->getLightProbe(); + if (pLightProbe != nullptr) + { + std::string fn = pLightProbe->getOrigTexture()->getSourceFilename(); + loadEnvMap(pRenderContext, fn); + } + if (!mpEnvProbe) mParams.useEnvMap = false; + + // Prepare UI list of materials. + mMaterialList.reserve(mpScene->getMaterialCount()); + for (uint32_t i = 0; i < mpScene->getMaterialCount(); i++) + { + auto mtl = mpScene->getMaterial(i); + std::string name = std::to_string(i) + ": " + mtl->getName(); + mMaterialList.push_back({ i, name }); + } + assert(mMaterialList.size() > 0); + } +} + +void BSDFViewer::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Update refresh flag if options that affect the output have changed. + if (mOptionsChanged) + { + Dictionary& dict = renderData.getDictionary(); + auto prevFlags = (Falcor::RenderPassRefreshFlags)(dict.keyExists(kRenderPassRefreshFlags) ? dict[Falcor::kRenderPassRefreshFlags] : 0u); + dict[Falcor::kRenderPassRefreshFlags] = (uint32_t)(prevFlags | Falcor::RenderPassRefreshFlags::RenderOptionsChanged); + mOptionsChanged = false; + } + + // Setup constants. + mParams.cameraViewportScale = std::tan(glm::radians(mParams.cameraFovY / 2.f)) * mParams.cameraDistance; + + // Set resources. + if (!mpSampleGenerator->setIntoProgramVars(mpViewerPass->getVars().get())) throw std::exception("Failed to bind sample generator"); + mpViewerPass["gOutput"] = renderData[kOutput]->asTexture(); + mpViewerPass["gPixelData"] = mPixelDataBuffer; + mpViewerPass["PerFrameCB"]["gParams"].setBlob(mParams); + + // Execute pass. + mpViewerPass->execute(pRenderContext, uvec3(mParams.frameDim, 1)); + + mPixelDataValid = false; + if (mParams.readback) + { + const PixelData* pData = static_cast(mPixelDataBuffer->map(Buffer::MapType::Read)); + mPixelData = *pData; + mPixelDataBuffer->unmap(); + mPixelDataValid = true; + + // Copy values from selected pixel. + mParams.texCoords = mPixelData.texC; + } + + mParams.frameCount++; +} + +void BSDFViewer::renderUI(Gui::Widgets& widget) +{ + bool dirty = false; + + dirty |= widget.checkbox("Enable BSDF slice viewer", mParams.sliceViewer); + widget.tooltip("Run BSDF slice viewer.\nOtherise the default mode shows a shaded sphere of the specified material.", true); + + if (mParams.sliceViewer) + { + widget.text("The current mode shows a slice of the BSDF.\n" + "The x-axis is theta_h (angle between H and normal)\n" + "and y-axis is theta_d (angle between H and wi/wo),\n" + "both in [0,pi/2] with origin in the lower/left."); + } + else + { + widget.text("The current mode shows a shaded unit sphere.\n" + "The coordinate frame is right-handed with xy\n" + "pointing right/up and +z towards the viewer.\n" + " "); + } + + auto mtlGroup = Gui::Group(widget, "Material", true); + if (mtlGroup.open()) + { + bool prevMode = mParams.useSceneMaterial; + mtlGroup.checkbox("Use scene material", mParams.useSceneMaterial); + mtlGroup.tooltip("Choose material in the dropdown below.\n\n" + "Left/right arrow keys step to the previous/next material in the list.", true); + + if (!mpScene) mParams.useSceneMaterial = false; + dirty |= ((bool)mParams.useSceneMaterial != prevMode); + + if (mParams.useSceneMaterial) + { + assert(mMaterialList.size() > 0); + dirty |= mtlGroup.dropdown("Materials", mMaterialList, mParams.materialID); + + dirty |= mtlGroup.checkbox("Normal mapping", mParams.useNormalMapping); + dirty |= mtlGroup.checkbox("Fixed tex coords", mParams.useFixedTexCoords); + dirty |= mtlGroup.var("Tex coords", mParams.texCoords, -std::numeric_limits::max(), std::numeric_limits::max(), 0.01f); + } + else + { + dirty |= mtlGroup.rgbColor("Base color", mParams.baseColor); + dirty |= mtlGroup.var("Roughness", mParams.linearRoughness, 0.f, 1.f, 1e-2f); + dirty |= mtlGroup.var("Metallic", mParams.metallic, 0.f, 1.f, 1e-2f); + } + + mtlGroup.release(); + } + + auto bsdfGroup = Gui::Group(widget, "BSDF", true); + if (bsdfGroup.open()) + { + dirty |= bsdfGroup.checkbox("Original Disney BRDF", mParams.originalDisney); + bsdfGroup.tooltip("When enabled uses the original Disney BRDF, otherwise the modified version by Frostbite.", true); + dirty |= bsdfGroup.checkbox("Enable diffuse", mParams.enableDiffuse); + dirty |= bsdfGroup.checkbox("Enable specular", mParams.enableSpecular, true); + + dirty |= bsdfGroup.checkbox("Use BRDF sampling", mParams.useBrdfSampling); + bsdfGroup.tooltip("When enabled uses BSDF importance sampling, otherwise hemispherical cosine-weighted sampling for verification purposes.", true); + dirty |= bsdfGroup.checkbox("Use pdf", mParams.usePdf); + bsdfGroup.tooltip("When enabled evaluates BRDF * NdotL / pdf explicitly for verification purposes.\nOtherwise the weight computed by the importance sampling is used.", true); + + dirty |= bsdfGroup.checkbox("Multiply BSDF slice by NdotL", mParams.applyNdotL); + bsdfGroup.tooltip("Note: This setting Only affects the BSDF slice viewer. NdotL is always enabled in lighting mode.", true); + + bsdfGroup.release(); + } + + auto lightGroup = Gui::Group(widget, "Light", true); + if (lightGroup.open()) + { + dirty |= lightGroup.var("Light intensity", mParams.lightIntensity, 0.f, std::numeric_limits::max(), 0.01f, false, "%.4f"); + dirty |= lightGroup.rgbColor("Light color", mParams.lightColor); + lightGroup.tooltip("Not used when environment map is enabled.", true); + + dirty |= lightGroup.checkbox("Show ground plane", mParams.useGroundPlane); + lightGroup.tooltip("When the ground plane is enabled, incident illumination from the lower hemisphere is zero.", true); + + // Directional lighting + dirty |= lightGroup.checkbox("Directional light", mParams.useDirectionalLight); + lightGroup.tooltip("When enabled a single directional light source is used, otherwise the light is omnidirectional.", true); + + if (mParams.useDirectionalLight) + { + mParams.useEnvMap = false; + dirty |= lightGroup.var("Light direction", mParams.lightDir, -std::numeric_limits::max(), std::numeric_limits::max(), 0.01f, false, "%.4f"); + } + + // Envmap lighting + if (mpEnvProbe) + { + dirty |= lightGroup.checkbox(("Envmap: " + mEnvProbeFilename).c_str(), mParams.useEnvMap); + lightGroup.tooltip("When enabled the specified environment map is used as light source. Enabling this option turns off directional lighting.", true); + + if (mParams.useEnvMap) + { + mParams.useDirectionalLight = false; + } + } + else + { + lightGroup.text("Envmap: N/A"); + } + + if (lightGroup.button("Load envmap")) + { + // Get file dialog filters. + auto filters = Bitmap::getFileDialogFilters(); + filters.push_back({ "hdr", "High Dynamic Range" }); + filters.push_back({ "dds", "DDS textures" }); + + std::string fn; + if (openFileDialog(filters, fn)) + { + // TODO: RenderContext* should maybe be a parameter to renderUI()? + auto pRenderContext = gpFramework->getRenderContext(); + if (loadEnvMap(pRenderContext, fn)) + { + mParams.useDirectionalLight = false; + mParams.useEnvMap = true; + dirty = true; + } + } + } + + lightGroup.release(); + } + + auto cameraGroup = Gui::Group(widget, "Camera", true); + if (cameraGroup.open()) + { + dirty |= cameraGroup.checkbox("Orthographic camera", mParams.orthographicCamera); + + if (!mParams.orthographicCamera) + { + dirty |= cameraGroup.var("Viewing distance", mParams.cameraDistance, 1.01f, std::numeric_limits::max(), 0.01f, false, "%.2f"); + cameraGroup.tooltip("This is the camera's distance to origin in projective mode. The scene has radius 1.0 so the minimum camera distance has to be > 1.0", true); + + dirty |= cameraGroup.var("Vertical FOV (degrees)", mParams.cameraFovY, 1.f, 179.f, 1.f, false, "%.2f"); + cameraGroup.tooltip("The allowed range is [1,179] degrees to avoid numerical issues.", true); + } + + cameraGroup.release(); + } + + auto pixelGroup = Gui::Group(widget, "Pixel data", true); + bool readTexCoords = mParams.useSceneMaterial && !mParams.useFixedTexCoords; + mParams.readback = readTexCoords || pixelGroup.open(); // Configure if readback is necessary + + if (pixelGroup.open()) + { + pixelGroup.var("Pixel", mParams.selectedPixel); + + if (mPixelDataValid) + { + pixelGroup.var("texC", mPixelData.texC, -std::numeric_limits::max(), std::numeric_limits::max(), 0.f, false, "%.4f"); + pixelGroup.var("baseColor", mPixelData.baseColor, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("diffuse", mPixelData.diffuse, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("specular", mPixelData.specular, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("roughness", mPixelData.linearRoughness, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.tooltip("This is the unmapped roughness parameters as specified in the content creation tool.", true); + pixelGroup.var("metallic", mPixelData.metallic, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("T", mPixelData.T, -1.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("B", mPixelData.B, -1.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("N", mPixelData.N, -1.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("wo", mPixelData.wo, -1.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("wi", mPixelData.wi, -1.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("output", mPixelData.output, 0.f, std::numeric_limits::max(), 0.f, false, "%.4f"); + } + else + { + pixelGroup.text("No data available"); + } + + pixelGroup.release(); + } + + //widget.dummy("#space3", vec2(1, 16)); + //dirty |= widget.checkbox("Debug switch", mParams.debugSwitch0); + + if (dirty) + { + mOptionsChanged = true; + } +} + +bool BSDFViewer::onMouseEvent(const MouseEvent& mouseEvent) +{ + if (mouseEvent.type == MouseEvent::Type::LeftButtonDown) + { + mParams.selectedPixel = glm::clamp((glm::ivec2)(mouseEvent.pos * (glm::vec2)mParams.frameDim), { 0,0 }, (glm::ivec2)mParams.frameDim - 1); + } + return false; +} + +bool BSDFViewer::onKeyEvent(const KeyboardEvent& keyEvent) +{ + if (keyEvent.type == KeyboardEvent::Type::KeyPressed) + { + if (keyEvent.key == KeyboardEvent::Key::Left || keyEvent.key == KeyboardEvent::Key::Right) + { + uint32_t id = mParams.materialID; + uint32_t lastId = mMaterialList.size() > 0 ? (uint32_t)mMaterialList.size() - 1 : 0; + if (keyEvent.key == KeyboardEvent::Key::Left) id = id > 0 ? id - 1 : lastId; + else if (keyEvent.key == KeyboardEvent::Key::Right) id = id < lastId ? id + 1 : 0; + + if (id != mParams.materialID) mOptionsChanged = true; // Triggers reset of accumulation + mParams.materialID = id; + return true; + } + } + return false; +} + +bool BSDFViewer::loadEnvMap(RenderContext* pRenderContext, const std::string& filename) +{ + auto pEnvProbe = EnvProbe::create(pRenderContext, filename); + if (!pEnvProbe) + { + logWarning("Failed to load environment map from " + filename); + return false; + } + + mpEnvProbe = pEnvProbe; + mEnvProbeFilename = getFilenameFromPath(mpEnvProbe->getEnvMap()->getSourceFilename()); + + auto pVars = mpViewerPass->getVars(); + if (!mpEnvProbe->setIntoConstantBuffer(pVars.get(), pVars->getConstantBuffer("PerFrameCB").get(), "gEnvProbe")) + { + throw std::exception("Failed to bind EnvProbe to program"); + } + + return true; +} diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang new file mode 100644 index 000000000..e87cc7724 --- /dev/null +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang @@ -0,0 +1,464 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "BSDFViewerParams.h" + +import Scene; +import Experimental.Scene.Material.MaterialShading; +import Experimental.Scene.Lights.EnvProbe; +import Utils.Sampling.SampleGenerator; +import Utils.Math.BitTricks; +import Utils.Math.MathHelpers; + +cbuffer PerFrameCB +{ + BSDFViewerParams gParams; + EnvProbe gEnvProbe; +} + +RWTexture2D gOutput; +RWStructuredBuffer gPixelData; + +static const float3 kGroundPlaneColor = float3(0.05f); + +struct SurfaceData +{ + ShadingData sd; + + // Additional fields we want to inspect that are not part of Falcor's ShadingData. + float3 baseColor; + float metallic; + float3 wi; +}; + + +/** Get normalized viewport coordinate. + The viewport is centered on the image with square aspect and height 1.0. The y-axis points down. + TODO: Change to a more standard definition. + \return Viewport coordinate. +*/ +float2 getViewportCoord(uint2 pixel) +{ + float2 p = pixel + float2(0.5f); + return (p - gParams.viewportOffset) * gParams.viewportScale; +} + +/** Setup geometric frame of reference for BRDF slice. + \param[in] uv Viewport coordinate in [0,1]. + \param[out] v Interpolated attributes for the point on the sphere. + \param[out] viewDir View direction. + \return Normalized incident direction (light vector). +*/ +float3 calculateSliceGeometry(float2 uv, out VertexData v, out float3 viewDir) +{ + // Setup local surface frame as T,B,N (right-handed). + v.posW = float3(0, 0, 0); + v.normalW = float3(0, 0, 1); + v.bitangentW = float3(0, 1, 0); + v.texC = gParams.texCoords; + v.faceNormalW = v.normalW; + + // Compute dot products. + // These are based on the axes in the 2D slice (theta_h, theta_d) with origin in lower-left corner. + // This is the same format as the slices in Burley et al. 2012, 2015. + float theta_h = uv.x * (M_PI / 2); + float theta_d = (1.f - uv.y) * (M_PI / 2); + + float NdotH = cos(theta_h); + float HdotL = cos(theta_d); // Note: HdotL = HdotV + + // Place the H vector at (0,0,1) to start. + // Compute L, V that are mirrored about the yz-plane. + float3 L = float3(sqrt(1.f - HdotL * HdotL), 0, HdotL); + float3 V = float3(-L.x, 0.f, L.z); + + // Rotate L, V about the x-axis by an angle theta_h. + float cos_h = NdotH; + float sin_h = sqrt(1 - NdotH * NdotH); + L = float3(L.x, cos_h * L.y - sin_h * L.z, sin_h * L.y + cos_h * L.z); + V = float3(V.x, cos_h * V.y - sin_h * V.z, sin_h * V.y + cos_h * V.z); + + // Return vectors. + viewDir = V; + return normalize(L); +} + +/** Ray-sphere intersection. + This function implements the standard analytic test and returns the closest hit. + \param[in] rayOrigin Ray origin. + \param[in] rayDir Ray direction (does not have to be normalized). + \param[in] center Sphere center. + \param[in] radius Sphere radius. + \param[in] intersectionPos Position on the sphere for the closest intersection (if any). + \return True if the ray intersects the sphere. +*/ +bool raySphereIntersection(float3 rayOrigin, float3 rayDir, float3 center, float radius, out float3 intersectionPos) +{ + // The sphere equation is ||P-C||^2 = r^2 and the ray P = A+tB. + // Solve for minimum positive t to find the closest intersection. + float3 oc = rayOrigin - center; + float a = dot(rayDir, rayDir); // = 1.0 if direction is normalized + float b = 2.f * dot(rayDir, oc); + float c = dot(oc, oc) - radius * radius; + float discriminant = b * b - 4.f * a * c; + + // Negative discriminant means ray missed sphere. + if (discriminant < 0.f) return false; + + // There are two solutions t0 and t1, but one or both may be negative. + float t0 = -b - sqrt(discriminant); + float t1 = -b + sqrt(discriminant); + float tc = t0 < 0.f ? t1 : t0; // tc is the closest hit we care about + if (tc < 0.f) return false; + + float t = tc / (2.f * a); + intersectionPos = rayOrigin + t * rayDir; + return true; +} + +/** Calculate sphere geometry for the given viewport coordinate. + \param[in] uv Viewport coordinate in [0,1]. + \param[out] v Interpolated attributes for the point on the sphere (if hit). + \param[out] rayDir Ray direction for the camera ray (normalized). + \return True if we're on the sphere. +*/ +bool calculateSphereGeometry(float2 uv, out VertexData v, out float3 rayDir) +{ + const float2 ndc = float2(2.f * uv.x - 1.f, -2.f * uv.y + 1.f); + + if (gParams.orthographicCamera) + { + // Calculate intersection with the unit sphere. + // The orthographic camera's viewport is +-1 units vertically so the sphere fits exactly. + float3 p = float3(ndc, 0); + float d = 1.f - p.x * p.x - p.y * p.y; + rayDir = float3(0, 0, -1); + + if (d < 0.f) return false; + p.z = sqrt(d); + v.posW = p; + } + else // Projective camera + { + // Setup camera ray and calculate ray-sphere intersection. + float3 origin = { 0, 0, gParams.cameraDistance }; + float3 target = float3(ndc * gParams.cameraViewportScale, 0); + rayDir = normalize(target - origin); + + float3 p; + if (!raySphereIntersection(origin, rayDir, float3(0), 1.f, p)) return false; + v.posW = p; + } + + // Setup surface attributes for the unit sphere. + v.normalW = v.posW; + v.bitangentW = perp_stark(v.normalW); // Make up a bitangent + v.faceNormalW = v.normalW; + + if (gParams.useFixedTexCoords) + { + v.texC = gParams.texCoords; + } + else + { + // Compute texture coords using cylindrical mapping of the visible hemisphere. + // We place u=0 on the left side and and u=1 on the right, and v=0 at the bottom and v=1 at the top. + float3 p = v.posW; + float texU = atan2(p.z, -p.x) / M_PI; + float texV = acos(-p.y) / M_PI; + v.texC = float2(texU, texV); + } + + return true; +} + +/** Prepare SurfaceData struct with material parameters. + All unused fields are initialized to their default values. +*/ +SurfaceData prepareMaterial(VertexData v, float3 viewDir) +{ + SurfaceData data = {}; + + if (gParams.useSceneMaterial) + { + // Setup Falcor's ShadingData based on scene material. + float3 camPosW = v.posW + viewDir; + ExplicitLodTextureSampler lod = { 0.f }; + data.sd = _prepareShadingData(v, gScene.materials[gParams.materialID], camPosW, lod, gParams.useNormalMapping); + + // Setup additional fields not currently available in ShadingData. + MaterialData m = gScene.materials[gParams.materialID]; + float4 baseColor = sampleTexture(m.resources.baseColor, m.resources.samplerState, v.texC, m.baseColor, EXTRACT_DIFFUSE_TYPE(m.flags), lod); + float4 spec = sampleTexture(m.resources.specular, m.resources.samplerState, v.texC, m.specular, EXTRACT_SPECULAR_TYPE(m.flags), lod); + + data.baseColor = baseColor.rgb; + if (EXTRACT_SHADING_MODEL(m.flags) == ShadingModelMetalRough) + { + data.metallic = spec.b; + } + } + else + { + ShadingData sd = {}; + + // Set geometric parameters. + sd.posW = v.posW; + sd.uv = v.texC; + sd.V = normalize(viewDir); + sd.N = normalize(v.normalW); + sd.B = normalize(v.bitangentW - sd.N * (dot(v.bitangentW, sd.N))); + sd.T = normalize(cross(sd.B, sd.N)); + sd.NdotV = dot(sd.N, sd.V); + sd.faceN = v.faceNormalW; + sd.frontFacing = dot(sd.V, sd.faceN) >= 0.f; + sd.doubleSided = false; + + // Set material parameters. + sd.diffuse = lerp(gParams.baseColor.rgb, float3(0), gParams.metallic); + sd.specular = lerp(float3(0.04f), gParams.baseColor.rgb, gParams.metallic); + sd.linearRoughness = gParams.linearRoughness; + sd.ggxAlpha = sd.linearRoughness * sd.linearRoughness; + sd.frontFacing = true; + sd.doubleSided = false; + + // Unused + sd.opacity = 1; + sd.occlusion = 1; + sd.IoR = 1; + + // Store outputs + data.sd = sd; + data.baseColor = gParams.baseColor; + data.metallic = gParams.metallic; + } + + return data; +} + +/** Returns the color to use for background pixels. + \param[in] uv Viewport coordinates. + \param[in] dir Normalized ray direction. +*/ +float3 evalBackground(float2 uv, float3 dir) +{ + if (gParams.useGroundPlane) + { + bool hitGround = gParams.orthographicCamera ? (uv.y >= 0.5f) : (dir.y < 0.f); + if (hitGround) return kGroundPlaneColor; + } + if (gParams.useDirectionalLight) return float3(0); + + float3 L = gParams.useEnvMap ? evalEnvProbe(gEnvProbe, dir) : gParams.lightColor; + return L * gParams.lightIntensity; +} + +/** Evaluates the incident lighting from a given direction. + If directional lighting is enabled, it can be assumed 'dir' is light's direction. +*/ +float3 evalLighting(float3 dir) +{ + if (gParams.useGroundPlane && dir.y < 0.f) + { + return float3(0.f); + } + + float3 L = gParams.useEnvMap ? evalEnvProbe(gEnvProbe, dir) : gParams.lightColor; + return L * gParams.lightIntensity; +} + +/** Helper function for evaluates the currently configured BRDF. +*/ +float3 evaluateBRDF(const ShadingData sd, const float3 L, bool useNdotL) +{ + float3 f; + if (gParams.originalDisney) + { + f = evalDisneyBRDF(sd, L, gParams.enableDiffuse, gParams.enableSpecular); + } + else + { + f = evalBRDF(sd, L, gParams.enableDiffuse, gParams.enableSpecular); + } + + // Apply dot(N,L) to result. + if (useNdotL) + { + float NdotL = saturate(dot(sd.N, L)); + f *= NdotL; + } + return f; +} + +/** Evaluates the BSDF slice for a given viewport coordinate. + \return Evaluated BSDF value. +*/ +float3 evalBSDFSlice(float2 uv, inout SurfaceData data) +{ + // Calculate geometry and incident/outgoing directions. + VertexData v; + float3 viewDir; + float3 lightDir = calculateSliceGeometry(uv, v, viewDir); + + // Setup shading data based on the current material. + data = prepareMaterial(v, viewDir); + data.wi = lightDir; + + // Evaluate BRDF at this point. + return evaluateBRDF(data.sd, data.wi, gParams.applyNdotL); +} + +/** Samples the BSDF to evaluate incident illumination. + This is done differently depending on the configuration. + \return True if a valid sample is returned. +*/ +bool sampleBSDF(const ShadingData sd, inout SampleGenerator sg, out BRDFSample s) +{ + if (gParams.useDirectionalLight) + { + // With directional light, disable BSDF sampling and just return a sample in the light's direction. + s.dir = -normalize(gParams.lightDir); + s.thp = evaluateBRDF(sd, s.dir, true); + s.pdf = dot(sd.N, s.dir) > 0.f ? 1.f : 0.f; // Set pdf to zero if light is backfacing. + } + else + { + if (gParams.useBrdfSampling) + { + // Draw BRDF sample. + if (gParams.originalDisney) + { + sampleDisneyBRDF(sd, sg, s); + } + else + { + sampleBRDF(sd, sg, s); + } + } + else + { + // Draw cosine sample over the hemisphere. + float2 u = sampleNext2D(sg); + float pdf = 0.f; + s.dir = sampleHemisphereCosine(sd, u, pdf); + s.thp = evaluateBRDF(sd, s.dir, false) * M_PI; // pdf = NdotL / pi + s.pdf = pdf; + } + } + + return s.pdf > 0.f; +} + +/** Evaluates the lit sphere for a given viewport coordinate. + The viewport shows an analytic sphere of the specified material at infinite distance. + When each pixel is evaluated using a random light direction and omnidirectional white light, + the result converges to the total reflectance (integral of BSDF times the dot(N,L) factor. + \return Outgoing radiance value. +*/ +float3 evalSphere(float2 uv, inout SurfaceData data, inout SampleGenerator sg) +{ + // Calculate the local surface frame. + VertexData v; + float3 rayDir; + if (!calculateSphereGeometry(uv, v, rayDir)) return evalBackground(uv, rayDir); + + // Setup shading data based on the current material. + data = prepareMaterial(v, -rayDir); + + float3 output = 0; + BRDFSample s = {}; + if (sampleBSDF(data.sd, sg, s)) + { + data.wi = s.dir; + float3 L = evalLighting(s.dir); + + // Use computed pdf explicitly (for debugging). + if (gParams.usePdf) + { + output = L * evaluateBRDF(data.sd, s.dir, true) / s.pdf; + } + else + { + output = L * s.thp; + } + } + + return output; +} + +/** BSDF viewer pass entry point. +*/ +[numthreads(16, 16, 1)] +void main(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + const uint2 pixel = dispatchThreadID.xy; + if (any(pixel >= gParams.frameDim)) return; + + SurfaceData data = {}; + float3 output = 0; + float2 uv = getViewportCoord(pixel); + + if (gParams.sliceViewer) + { + if (all(uv >= 0.f && uv < 1.f)) + { + output = evalBSDFSlice(uv, data); + } + } + else + { + // Create pseudorandom number generator. + SampleGenerator sg = SampleGenerator.create(pixel, gParams.frameCount); + output = evalSphere(uv, data, sg); + } + + // DEBUG + //if (gParams.debugSwitch0) + //{ + // if (sd.N.z < 0.f) output = float3(1, 0, 0); + //} + + // Write output data. + gOutput[pixel] = float4(output, 1); + + if (gParams.readback && all(pixel == gParams.selectedPixel)) + { + PixelData px; + px.texC = data.sd.uv; + px.baseColor = data.baseColor; + px.diffuse = data.sd.diffuse; + px.specular = data.sd.specular; + px.linearRoughness = data.sd.linearRoughness; + px.metallic = data.metallic; + px.N = data.sd.N; + px.T = data.sd.T; + px.B = data.sd.B; + px.wo = data.sd.V; + px.wi = data.wi; + px.output = output; + gPixelData[0] = px; + } +} diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.h b/Source/RenderPasses/BSDFViewer/BSDFViewer.h new file mode 100644 index 000000000..b8fb5a91f --- /dev/null +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.h @@ -0,0 +1,80 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "FalcorExperimental.h" +#include "BSDFViewerParams.h" +#include "Utils/Sampling/SampleGenerator.h" +#include "Experimental/Scene/Lights/EnvProbe.h" + +using namespace Falcor; + +class BSDFViewer : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + /** Create a new object + */ + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return sDesc; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; + + static const char* sDesc; + +private: + BSDFViewer() = default; + bool init(const Dictionary& dict); + bool loadEnvMap(RenderContext* pRenderContext, const std::string& filename); + + // Internal state + Scene::SharedPtr mpScene; ///< Loaded scene if any, nullptr otherwise. + EnvProbe::SharedPtr mpEnvProbe; ///< Environment map if loaded, nullptr otherwise. + std::string mEnvProbeFilename; ///< Filename of loaded environment map, or empty string otherwise. + + BSDFViewerParams mParams; ///< Parameters shared with the shaders. + SampleGenerator::SharedPtr mpSampleGenerator; ///< Random number generator for the integrator. + bool mOptionsChanged = false; + + StructuredBuffer::SharedPtr mPixelDataBuffer; ///< Buffer for read back of data for the selected pixel. + PixelData mPixelData; ///< Pixel data for the selected pixel (if valid). + bool mPixelDataValid = false; + + ComputePass::SharedPtr mpViewerPass; + + // UI variables + Gui::DropdownList mMaterialList; +}; diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj b/Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj new file mode 100644 index 000000000..c085cc9d2 --- /dev/null +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj @@ -0,0 +1,104 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {B219C161-94D2-4DCB-A75D-E6A0906F534D} + Win32Proj + BSDFViewer + 10.0.17763.0 + BSDFViewer + + + + + + + + + + + + + + + + + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + + + DynamicLibrary + true + v141 + Unicode + Data\RenderPasses\$(ProjectName) + + + DynamicLibrary + false + v141 + true + Unicode + Data\RenderPasses\$(ProjectName) + + + + + + + true + + + false + + + + + + Level3 + Disabled + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj.filters b/Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj.filters new file mode 100644 index 000000000..9c4f64210 --- /dev/null +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewerParams.h b/Source/RenderPasses/BSDFViewer/BSDFViewerParams.h new file mode 100644 index 000000000..450310fe9 --- /dev/null +++ b/Source/RenderPasses/BSDFViewer/BSDFViewerParams.h @@ -0,0 +1,114 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Data/HostDeviceData.h" + +BEGIN_NAMESPACE_FALCOR + +/** BSDFViewer parameters shared between host and device. + Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. + Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. + https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx +*/ +struct BSDFViewerParams +{ + uint2 frameDim = { 0, 0 }; ///< Frame buffer dimension in pixels. + uint frameCount = 0; ///< Frames rendered. + int sliceViewer = 0; ///< Enable BSDF slice viewer, otherwise material viewer. + + float2 viewportOffset; ///< Top-left corner of viewport in pixels. + float2 viewportScale; ///< 1/Size of viewport in pixels. + + // Material parameters + uint materialID = 0; ///< Scene material ID. + int useSceneMaterial = 1; ///< Use material from scene, otherwise use manually specified BSDF parameters. + int useNormalMapping = 0; ///< Use normal mapping. + int useFixedTexCoords = 0; ///< Use fixed texture coordinates. + + float3 baseColor = { 0.5f, 0.5f, 0.5f }; ///< Material base color. + float linearRoughness = 1.f; ///< Linear roughness in [0,1]. + + float metallic = 0.f; ///< Metallic factor in [0,1]. + float2 texCoords = { 0.f, 0.f }; ///< Texture coordinates to use when 'useFixedTexCoords' is true. + float _pad1; + + // BSDF settings + int enableDiffuse = 1; ///< Enable diffuse lobe. + int enableSpecular = 1; ///< Enable specular lobe. + int applyNdotL = 0; ///< Multiply BSDF by NdotL in slice viewer. + int originalDisney = 0; ///< Evaluate the original Disney BRDF, otherwise Frostbite's version. + + int useBrdfSampling = 1; ///< Use BRDF importance sampling. + int usePdf = 0; ///< Use BRDF sampling pdf explicitly, otherwise the precomputed weight (for debugging). + int _pad2; + + // Lighting settings + int useGroundPlane = 0; ///< Draw a ground plane. + int useEnvMap = 0; ///< Use environment light (as opposed to omnidirectional). + int2 _pad3; + + float lightIntensity = 1.f; ///< Light intensity, acts as a multiplier for the light color. + float3 lightColor = { 1.f, 1.f, 1.f }; ///< Light color. + + int useDirectionalLight = 0; ///< Use directional light (as opposed to omnidirectional/envmap). + float3 lightDir = { 0.f, 0.f, -1.f }; ///< Light direction to use when 'useDirectionalLight' is true (note: not normalized). + + // Camera settings + int orthographicCamera = 0; ///< Use orthographic camera. + float cameraDistance = 1.5f; ///< Camera distance from origin in projective mode. Valid range is (1,+inf). + float cameraFovY = 90.f; ///< Camera vertical field-of-view in degrees. + float cameraViewportScale; ///< Camera viewport scale (= tan(fovY/2)*distance) computed at runtime in projective mode. + + // Misc settings + int readback = 1; ///< True if we should read back data for the selected pixel. + int2 selectedPixel = { 0, 0 }; ///< Pixel coordinates selected for readback. + int _pad5; + + //int debugSwitch0 = 0; + //int3 _pad6; +}; + +/** Struct for readback of per-pixel data. +*/ +struct PixelData +{ + float2 texC; + float3 baseColor; + float3 diffuse; + float3 specular; + float linearRoughness; + float metallic; + float3 T; + float3 B; + float3 N; + float3 wo; + float3 wi; + float3 output; +}; + +END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp new file mode 100644 index 000000000..ad544fc91 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "ComparisonPass.h" + +namespace +{ + const std::string kSplitLocation = "splitLocation"; + const std::string kDividerSize = "dividerSize"; + const std::string kShowTextLabels = "showTextLabels"; + const std::string kLeftLabel = "leftLabel"; + const std::string kRightLabel = "rightLabel"; + + const std::string kLeftInput = "leftInput"; + const std::string kRightInput = "rightInput"; + const std::string kOutput = "output"; + + // Divider colors + vec4 kColorUnselected = vec4(0, 0, 0, 1); + vec4 kColorSelected = vec4(1, 1, 1, 1); + + // A simple character array representing a 16x16 grayscale arrow + const unsigned char kArrowArray[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 87, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 255, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 255, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, 34, 0, + 31, 158, 156, 156, 156, 156, 156, 156, 156, 146, 212, 255, 255, 255, 255, 34, + 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, + 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, + 31, 158, 156, 156, 156, 156, 156, 156, 156, 146, 212, 255, 255, 255, 255, 33, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 255, 255, 255, 34, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 255, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255 ,255, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 12, 0, 0, 0, 0, 0, 0 + }; +} + +ComparisonPass::ComparisonPass() +{ + mpArrowTex = Texture::create2D(16, 16, ResourceFormat::R8Unorm, 1, Texture::kMaxPossible, kArrowArray); + mClock = gpFramework->getGlobalClock(); +} + +void ComparisonPass::parseDictionary(const Dictionary& dict) +{ + for (const auto& v : dict) + { + if (v.key() == kSplitLocation) mSplitLoc = v.val(); + if (v.key() == kDividerSize) mDividerSize = v.val(); + if (v.key() == kShowTextLabels) mShowLabels = v.val(); + if (v.key() == kLeftLabel) + { + std::string str = v.val(); + mLeftLabel = str; + } + if (v.key() == kRightLabel) + { + std::string str = v.val(); + mRightLabel = str; + } + else logWarning("Unknown field `" + v.key() + "` in a ComparisonPass dictionary"); + } +} + +Dictionary ComparisonPass::getScriptingDictionary() +{ + Dictionary dict; + dict[kSplitLocation] = mSplitLoc; + dict[kDividerSize] = mDividerSize; + dict[kShowTextLabels] = mShowLabels; + dict[kLeftLabel] = mLeftLabel; + dict[kRightLabel] = mRightLabel; + return dict; +} + +RenderPassReflection ComparisonPass::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + r.addInput(kLeftInput, "Left side image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); + r.addInput(kRightInput, "Right side image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); + r.addOutput(kOutput, "Output image").bindFlags(Falcor::Resource::BindFlags::RenderTarget).texture2D(0, 0); + return r; +} + +void ComparisonPass::execute(RenderContext* pContext, const RenderData& renderData) +{ + // Get references to our input, output, and temporary accumulation texture + pLeftSrcTex = renderData[kLeftInput]->asTexture(); + pRightSrcTex = renderData[kRightInput]->asTexture(); + pDstFbo = Fbo::create({ renderData[kOutput]->asTexture() }); + + // If we haven't initialized the split location, split the screen in half by default + if (mSplitLoc < 0) mSplitLoc = 0.5f; + + // Set shader parameters + mpSplitShader["GlobalCB"]["gSplitLocation"] = int32_t(mSplitLoc * renderData.getDefaultTextureDims().x); + mpSplitShader["GlobalCB"]["gDividerSize"] = mDividerSize; + mpSplitShader["GlobalCB"]["gDividerColor"] = mMouseOverDivider ? kColorSelected : kColorUnselected; + mpSplitShader["GlobalCB"]["gMousePosition"] = mMousePos; + mpSplitShader["GlobalCB"]["gDrawArrows"] = mDrawArrows && mMouseOverDivider; + mpSplitShader["gLeftInput"] = mSwapSides ? pRightSrcTex : pLeftSrcTex; + mpSplitShader["gRightInput"] = mSwapSides ? pLeftSrcTex : pRightSrcTex; + mpSplitShader["gArrowTex"] = mpArrowTex; + + // Execute the accumulation shader + mpSplitShader->execute(pContext, pDstFbo); + + // Render some labels + if (mShowLabels) + { + // Can optionally only show the labels when hovering over the divider. + if (!mLabelsOnlyWhenHovering || mMouseOverDivider) + { + int32_t screenLoc = int32_t(mSplitLoc * renderData.getDefaultTextureDims().x); + + // Draw text labeling the right side image + std::string rightSide = mSwapSides ? mLeftLabel : mRightLabel; + TextRenderer::render(pContext, rightSide.c_str(), pDstFbo, vec2(screenLoc + 16, 16)); + + // Draw text labeling the left side image + std::string leftSide = mSwapSides ? mRightLabel : mLeftLabel; + uint32_t leftLength = uint32_t(leftSide.length()) * 9; + TextRenderer::render(pContext, leftSide.c_str(), pDstFbo, vec2(screenLoc - 16 - leftLength, 16)); + } + } +} + +bool ComparisonPass::onMouseEvent(const MouseEvent& mouseEvent) +{ + // If we have the divider grabbed, claim *all* mouse movements for ourself + bool handled = mDividerGrabbed; + + // Find out where on the screen we are + mMousePos = ivec2(mouseEvent.screenPos.x, mouseEvent.screenPos.y); + + // If we're outside the window, stop. + mMousePos = glm::clamp(mMousePos, ivec2(0, 0), ivec2(pDstFbo->getWidth() - 1, pDstFbo->getHeight() - 1)); + + // Actually process our events + if (mMouseOverDivider && mouseEvent.type == MouseEvent::Type::LeftButtonDown) + { + mDividerGrabbed = true; + handled = true; + + if (mClock.now() - mTimeOfLastClick < 0.1f) mSplitLoc = 0.5f; + else mTimeOfLastClick = mClock.now(); + } + else if (mDividerGrabbed) + { + if (mouseEvent.type == MouseEvent::Type::LeftButtonUp) + { + mDividerGrabbed = false; + handled = true; + } + else if (mouseEvent.type == MouseEvent::Type::Move) + { + mSplitLoc = (float)mMousePos.x / (float)pDstFbo->getWidth(); + handled = true; + } + } + + // Update whether the mouse if over the divider. To ensure selecting the slider isn't a pain, + // have a minimum landing size (13 pixels, 2*6+1) that counts as hovering over the slider. + mMouseOverDivider = (glm::abs(int32_t(mSplitLoc * pDstFbo->getWidth()) - mMousePos.x) < glm::max(6, int32_t(mDividerSize))); + + return handled; +} + +void ComparisonPass::renderUI(Gui::Widgets& widget) +{ + widget.var("Split location", mSplitLoc, 0.0f, 1.0f, 0.001f); + widget.checkbox("Swap Sides", mSwapSides); + widget.checkbox("Show Arrows", mDrawArrows, true); + widget.checkbox("Show Labels", mShowLabels); + if (mShowLabels) + { + widget.checkbox("Show Only On Hover", mLabelsOnlyWhenHovering, true); + } +} diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.h b/Source/RenderPasses/DebugPasses/ComparisonPass.h new file mode 100644 index 000000000..c59d13574 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.h @@ -0,0 +1,78 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" + +using namespace Falcor; + +class ComparisonPass : public RenderPass +{ +public: + using SharedPtr = std::shared_ptr; + + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; + virtual void renderUI(Gui::Widgets& widget) override; + + void parseDictionary(const Dictionary& dict); + +protected: + ComparisonPass(); + virtual void createProgram() = 0; + + FullScreenPass::SharedPtr mpSplitShader; + Texture::SharedPtr pLeftSrcTex; + Texture::SharedPtr pRightSrcTex; + Fbo::SharedPtr pDstFbo; + Texture::SharedPtr mpArrowTex; ///< A texture storing a 16x16 grayscale arrow + + // Screen parameters + bool mSwapSides = false; ///< Is the left input on the left side + + // Mouse parameters + bool mMouseOverDivider = false; ///< Is the mouse over the divider? + ivec2 mMousePos = ivec2(0, 0); ///< Where was mouse in last mouse event processed + bool mDividerGrabbed = false; ///< Are we grabbing the divider? + + // Divider parameters + float mSplitLoc = -1.0f; ///< Location of the divider as a fraction of screen width, values < 0 are initialized to 0.5 + uint32_t mDividerSize = 2; ///< Size of the divider (in pixels: 2*mDividerSize+1) + bool mDrawArrows = false; ///< When hovering over divider, show arrows? + + // Label Parameters + bool mShowLabels = false; ///< Show text labels for two images? + bool mLabelsOnlyWhenHovering = false; ///< Show text always, or only when hovering over divider? + std::string mLeftLabel = "Left side"; ///< Left label. Set in Python script with "leftLabel" + std::string mRightLabel = "Right side"; ///< Right label. Set in Python script with "rightLabel" + + // Double-click detection Parameters + Clock mClock; ///< Global clock used to track click times + double mTimeOfLastClick = 0; ///< Time since mouse was last clicked +}; diff --git a/Source/RenderPasses/DebugPasses/Data/Comparison.ps.slang b/Source/RenderPasses/DebugPasses/Data/Comparison.ps.slang new file mode 100644 index 000000000..4780f8f76 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/Data/Comparison.ps.slang @@ -0,0 +1,87 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +cbuffer GlobalCB +{ + int gSplitLocation; // X-value of the current split location to display + uint gDividerSize; // How wide should the divider be? + float4 gDividerColor; // What color should the divider be this frame? + int2 gMousePosition; // What is the current position of the mouse? (Used to position arrows) + bool gDrawArrows; // Should we draw arrows to the left & right of the divider? + uint gLeftBound; // How many pixels from the left side should the comparison window start? +}; + +Texture2D gLeftInput; +Texture2D gRightInput; +Texture2D gArrowTex; + +interface ICalcPixelColor +{ + float2x4 calcColors(uint2 pixelPos); +}; + +float4 compare(float2 texC, float4 pos, C calc) +{ + // Get the two sides of the image + uint2 pixelPos = (uint2) pos.xy; + float2x4 colors = calc.calcColors(pixelPos); + float4 leftColor = colors[0]; + float4 rightColor = colors[1]; + + // Combine two images, depending on which side of the split we're on. Clamp inputs to minimum 0. + float4 color = max((pixelPos.x < gSplitLocation) ? leftColor : rightColor, float4(0, 0, 0, 0)); + + // Overlay divider onto the image + if (abs(int(pixelPos.x) - gSplitLocation) < gDividerSize) + { + color = gDividerColor; + } + + // Draw arrows + if (gDrawArrows) + { + uint2 dims; + gArrowTex.GetDimensions(dims.x, dims.y); + int2 arrow1Coord = int2(pixelPos.x - 5 - gSplitLocation, pixelPos.y - gMousePosition.y + 8); + if (all(clamp(arrow1Coord, int2(0, 0), int2(dims)) == arrow1Coord)) + { + float arrowAlpha = gArrowTex[arrow1Coord]; + color = gDividerColor * arrowAlpha + ((arrowAlpha < 1.0) ? color * (1 - arrowAlpha) : float4(0, 0, 0, 0)); + } + + int2 arrow2Coord = int2(pixelPos.x + 21 - gSplitLocation, pixelPos.y - gMousePosition.y + 8); + if (all(clamp(arrow2Coord, int2(0, 0), int2(dims)) == arrow2Coord)) + { + arrow2Coord.x = abs(arrow2Coord.x - 15); + float arrowAlpha = gArrowTex[arrow2Coord]; + color = gDividerColor * arrowAlpha + ((arrowAlpha < 1.0) ? color * (1 - arrowAlpha) : float4(0, 0, 0, 0)); + } + } + + return color; +} diff --git a/Samples/Effects/AmbientOcclusion/Data/ApplyAO.ps.hlsl b/Source/RenderPasses/DebugPasses/Data/InvalidPixelDetection.ps.slang similarity index 72% rename from Samples/Effects/AmbientOcclusion/Data/ApplyAO.ps.hlsl rename to Source/RenderPasses/DebugPasses/Data/InvalidPixelDetection.ps.slang index fc77fa35e..b32761689 100644 --- a/Samples/Effects/AmbientOcclusion/Data/ApplyAO.ps.hlsl +++ b/Source/RenderPasses/DebugPasses/Data/InvalidPixelDetection.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,14 +25,28 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import Helpers; -SamplerState gSampler; +Texture2D gTexture; -Texture2D gColor; -Texture2D gAOMap; +bool4 is_nan(float4 f) +{ + uint4 expMask = 0x7f800000; + uint4 mantissaMask = (~expMask) & (~0x80000000); + uint4 u = asuint(f); + if(all((expMask & u) != expMask)) return false; + if(any(mantissaMask & u)) return true; + return false; +} float4 main(float2 texC : TEXCOORD) : SV_TARGET0 { - return applyAmbientOcclusion(gColor.SampleLevel(gSampler, texC, 0), gAOMap, gSampler, texC); + uint2 uv; + gTexture.GetDimensions(uv.x, uv.y); + int2 xy = int2(uv * texC); + float4 value = gTexture.Load(int3(xy, 0)); + if (any(is_nan(value))) + return float4(1, 0, 0, 1); + else if (any(isinf(value))) + return float4(0, 1, 0, 1); + return float4(0, 0, 0, 1); } diff --git a/Samples/Raytracing/PathTracer/Data/Accumulate.slang b/Source/RenderPasses/DebugPasses/Data/SideBySide.ps.slang similarity index 76% rename from Samples/Raytracing/PathTracer/Data/Accumulate.slang rename to Source/RenderPasses/DebugPasses/Data/SideBySide.ps.slang index 03fe366b1..ccc14ab1b 100644 --- a/Samples/Raytracing/PathTracer/Data/Accumulate.slang +++ b/Source/RenderPasses/DebugPasses/Data/SideBySide.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,25 +25,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -import ShaderCommon; +import "Comparison.ps"; -cbuffer PerFrameCB +struct SideBySide : ICalcPixelColor { - uint gAccumCount; -} - -IRWTexture2D gAccumBuf; -ITexture2D gCurFrame; + float2x4 calcColors(uint2 pixelPos) + { + float2x4 colors; + colors[0] = gLeftInput[uint2(pixelPos.x + gLeftBound, pixelPos.y)]; + colors[1] = gRightInput[uint2(int(pixelPos.x) - gSplitLocation + int(gLeftBound), pixelPos.y)]; + return colors; + } +}; float4 main(float2 texC : TEXCOORD, float4 pos : SV_Position) : SV_Target0 { - uint2 pixelPos = (uint2) pos.xy; - float4 curColor = gCurFrame[pixelPos]; - float4 prevColor = gAccumBuf[pixelPos]; - - float4 accumColor = (gAccumCount * prevColor + curColor) / (gAccumCount + 1); - - // Overwrite our intermediate accum buffer; also output to the destination FBO - gAccumBuf[pixelPos] = accumColor; - return accumColor; + SideBySide comp = { }; + return compare(texC, pos, comp); } diff --git a/Samples/Core/ShaderBuffers/Data/ShaderBuffers.cs.hlsl b/Source/RenderPasses/DebugPasses/Data/SplitScreen.ps.slang similarity index 77% rename from Samples/Core/ShaderBuffers/Data/ShaderBuffers.cs.hlsl rename to Source/RenderPasses/DebugPasses/Data/SplitScreen.ps.slang index a3fa31192..fdcc6a4d1 100644 --- a/Samples/Core/ShaderBuffers/Data/ShaderBuffers.cs.hlsl +++ b/Source/RenderPasses/DebugPasses/Data/SplitScreen.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,23 +25,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -struct LightCB -{ - float3 vec3Val; // We're using 2 values. [0]: worldDir [1]: intensity -}; +import "Comparison.ps"; -StructuredBuffer gLightIn; -AppendStructuredBuffer gLightOut; - -[numthreads(1, 1, 1)] -void main() +struct InteractiveImageComparison : ICalcPixelColor { - uint numLights = 0; - uint stride; - gLightIn.GetDimensions(numLights, stride); - - for (uint i = 0; i < numLights; i++) + float2x4 calcColors(uint2 pixelPos) { - gLightOut.Append(gLightIn[i]); + float2x4 colors; + colors[0] = gLeftInput[pixelPos]; + colors[1] = gRightInput[pixelPos]; + return colors; } +}; + +float4 main(float2 texC : TEXCOORD, float4 pos : SV_Position) : SV_Target0 +{ + InteractiveImageComparison comp = { }; + return compare(texC, pos, comp); } diff --git a/Framework/Source/Graphics/Paths/MovableObject.h b/Source/RenderPasses/DebugPasses/DebugPasses.cpp similarity index 67% rename from Framework/Source/Graphics/Paths/MovableObject.h rename to Source/RenderPasses/DebugPasses/DebugPasses.cpp index 5137a6b59..fb1cb97d8 100644 --- a/Framework/Source/Graphics/Paths/MovableObject.h +++ b/Source/RenderPasses/DebugPasses/DebugPasses.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,24 +26,18 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "glm/vec3.hpp" +#include "SplitScreenPass/SplitScreenPass.h" +#include "InvalidPixelDetectionPass/InvalidPixelDetectionPass.h" +#include "SideBySidePass/SideBySidePass.h" -namespace Falcor +extern "C" __declspec(dllexport) const char* getProjDir() { - class ObjectPath; + return PROJECT_DIR; +} - class IMovableObject : public std::enable_shared_from_this - { - public: - using SharedPtr = std::shared_ptr; - using SharedConstPtr = std::shared_ptr; - - virtual void move(const glm::vec3& position, const glm::vec3& target, const glm::vec3& up) = 0; - const ObjectPath* getAttachedPath() const { return mpPath; } - - private: - friend class ObjectPath; - void attachPath(const ObjectPath* pPath) { mpPath = pPath; } - const ObjectPath* mpPath = nullptr; - }; -} \ No newline at end of file +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("SplitScreenPass", "Allows the user to split the screen between two inputs.", SplitScreenPass::create); + lib.registerClass("InvalidPixelDetectionPass", "Pass that marks all NaN pixels red and Inf pixels green in an image", InvalidPixelDetectionPass::create); + lib.registerClass("SideBySidePass", "Allows the user to compare two images side-by-side", SideBySidePass::create); +} diff --git a/Samples/ForwardRenderer/ForwardRenderer.vcxproj b/Source/RenderPasses/DebugPasses/DebugPasses.vcxproj similarity index 64% rename from Samples/ForwardRenderer/ForwardRenderer.vcxproj rename to Source/RenderPasses/DebugPasses/DebugPasses.vcxproj index dcc5b760c..45f52e91b 100644 --- a/Samples/ForwardRenderer/ForwardRenderer.vcxproj +++ b/Source/RenderPasses/DebugPasses/DebugPasses.vcxproj @@ -11,48 +11,47 @@ - - - + + + + + - - + + + + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - + - - Document - + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + - - Document - + + + - {ADDD1F96-AE44-40BA-9942-0F056F96FA4B} + {CA8CBD7E-4E98-4CEA-A53C-8C18A361C8E7} Win32Proj FeatureDemo 10.0.17763.0 - ForwardRenderer + DebugPasses - Application + DynamicLibrary true v141 Unicode - Application + DynamicLibrary false v141 true @@ -62,10 +61,10 @@ - + - + @@ -80,7 +79,10 @@ Level3 Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) + WIN32;PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + stdcpp17 + true Windows @@ -95,7 +97,10 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + WIN32;PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + stdcpp17 + true Windows diff --git a/Source/RenderPasses/DebugPasses/DebugPasses.vcxproj.filters b/Source/RenderPasses/DebugPasses/DebugPasses.vcxproj.filters new file mode 100644 index 000000000..f9cf5e4d4 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/DebugPasses.vcxproj.filters @@ -0,0 +1,58 @@ + + + + + {af379eba-da22-4554-b098-3b5a60dd6590} + + + {230d611e-fde4-40ca-b0d3-3e00481d4d53} + + + {65347d22-d193-41fa-947a-8b224c27da0e} + + + {cfc05c4d-c4b8-4552-83b8-4d55466cbbef} + + + + + Data + + + + + InvalidPixelDetectionPass + + + + SplitScreenPass + + + SideBySidePass + + + + + + InvalidPixelDetectionPass + + + + SplitScreenPass + + + SideBySidePass + + + + + Data + + + Data + + + Data + + + \ No newline at end of file diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp new file mode 100644 index 000000000..f97e30ec2 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "InvalidPixelDetectionPass.h" + +namespace +{ + const std::string kSrc = "src"; + const std::string kDst = "dst"; +} + +InvalidPixelDetectionPass::InvalidPixelDetectionPass() +{ + mpInvalidPixelDetectPass = FullScreenPass::create("Data\\InvalidPixelDetection.ps.slang"); + mpFbo = Fbo::create(); +} + +InvalidPixelDetectionPass::SharedPtr InvalidPixelDetectionPass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new InvalidPixelDetectionPass()); + return pPass; +} + +RenderPassReflection InvalidPixelDetectionPass::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + mReady = false; + if (compileData.connectedResources.getFieldCount() > 0) + { + const RenderPassReflection::Field* edge = compileData.connectedResources.getField(kSrc); + RenderPassReflection::Field::Type srcType = edge->getType(); + ResourceFormat srcFormat = edge->getFormat(); + uint32_t srcWidth = edge->getWidth(); + uint32_t srcHeight = edge->getHeight(); + uint32_t srcDepth = edge->getDepth(); + uint32_t srcSampleCount = edge->getSampleCount(); + uint32_t srcMipCount = edge->getMipCount(); + uint32_t srcArraySize = edge->getArraySize(); + + auto formatField = [=](RenderPassReflection::Field& f) { + return f.resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); + }; + + formatField(r.addInput(kSrc, "Input image to be checked")).format(srcFormat); + formatField(r.addOutput(kDst, "Output where pixels are red if NaN, green if Inf, and black otherwise")); + mReady = true; + } + else + { + r.addInput(kSrc, "Input image to be checked"); + r.addOutput(kDst, "Output where pixels are red if NaN, green if Inf, and black otherwise"); + } + return r; +} + +void InvalidPixelDetectionPass::compile(RenderContext* pContext, const CompileData& compileData) +{ + if (!mReady) throw std::runtime_error("InvalidPixelDetectionPass::compile - missing incoming reflection data"); +} + +void InvalidPixelDetectionPass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + mpInvalidPixelDetectPass["gTexture"] = renderData[kSrc]->asTexture(); + mpFbo->attachColorTarget(renderData[kDst]->asTexture(), 0); + mpInvalidPixelDetectPass->getState()->setFbo(mpFbo); + mpInvalidPixelDetectPass->execute(pRenderContext, mpFbo); +} diff --git a/Samples/Core/ComputeShader/ComputeShader.h b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h similarity index 66% rename from Samples/Core/ComputeShader/ComputeShader.h rename to Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h index d25b919ae..4c5f8f507 100644 --- a/Samples/Core/ComputeShader/ComputeShader.h +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,22 +30,23 @@ using namespace Falcor; -class ComputeShader : public Renderer +class InvalidPixelDetectionPass : public RenderPass { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pContext, const Fbo::SharedPtr& pTargetFbo) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height); + using SharedPtr = std::shared_ptr; -private: - ComputeProgram::SharedPtr mpProg; - ComputeState::SharedPtr mpState; - bool mbPixelate = false; - ComputeVars::SharedPtr mpProgVars; - Texture::SharedPtr mpImage; + /** Create a new object + */ + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return "Pass that marks all NaN pixels red and Inf pixels green in an image"; } + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; - Texture::SharedPtr mpTmpTexture; - void loadImage(SampleCallbacks* pSample); - void loadImageFromFile(SampleCallbacks* pSample, std::string file); +private: + InvalidPixelDetectionPass(); + FullScreenPass::SharedPtr mpInvalidPixelDetectPass; + Fbo::SharedPtr mpFbo; + bool mReady = false; }; diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp new file mode 100644 index 000000000..8b74461d9 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "SideBySidePass.h" + +namespace +{ + // Where is our shader located? + const std::string kSplitShader = "Data\\SideBySide.ps.slang"; +} + +SideBySidePass::SideBySidePass() { createProgram(); } + +SideBySidePass::SharedPtr SideBySidePass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new SideBySidePass()); + pPass->parseDictionary(dict); + return pPass; +} + +void SideBySidePass::createProgram() +{ + // Create our shader that splits the screen. + mpSplitShader = FullScreenPass::create(kSplitShader); +} + +void SideBySidePass::execute(RenderContext* pContext, const RenderData& renderData) +{ + mpSplitShader["GlobalCB"]["gLeftBound"] = mImageLeftBound; + ComparisonPass::execute(pContext, renderData); +} + +void SideBySidePass::renderUI(Gui::Widgets& widget) +{ + ComparisonPass::renderUI(widget); + widget.slider("View Slider", mImageLeftBound, 0u, pDstFbo->getWidth() - pDstFbo->getWidth() / 2); +} diff --git a/Samples/Core/ProjectTemplate/ProjectTemplate.h b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h similarity index 69% rename from Samples/Core/ProjectTemplate/ProjectTemplate.h rename to Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h index 5a3542ed5..b9209ab62 100644 --- a/Samples/Core/ProjectTemplate/ProjectTemplate.h +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,20 +27,22 @@ ***************************************************************************/ #pragma once #include "Falcor.h" +#include "../ComparisonPass.h" using namespace Falcor; -class ProjectTemplate : public Renderer +class SideBySidePass : public ComparisonPass { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onShutdown(SampleCallbacks* pSample) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onDataReload(SampleCallbacks* pSample) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; + using SharedPtr = std::shared_ptr; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + virtual std::string getDesc() override { return "Allows the user to compare two inputs side-by-side."; } + virtual void execute(RenderContext* pContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; private: + SideBySidePass(); + virtual void createProgram() override; + uint32_t mImageLeftBound = 0; ///< Location of output left side in original input image in pixels }; diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/Testing/testSideBySide.py b/Source/RenderPasses/DebugPasses/SideBySidePass/Testing/testSideBySide.py new file mode 100644 index 000000000..92732a3ce --- /dev/null +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/Testing/testSideBySide.py @@ -0,0 +1,20 @@ +def test_side_comparison(): + loadRenderPassLibrary("DebugPasses.dll") + imageLoaderA = RenderPass("ImageLoader", {'filename': 'Cubemaps\\Sorsele3\\posz.jpg', 'mips': False, 'srgb': False}) + imageLoaderB = RenderPass("ImageLoader", {'filename': 'Cubemaps\\Sorsele3\\posz.jpg', 'mips': False, 'srgb': True}) + sideComparison = RenderPass("SideBySidePass") + + graph = RenderGraph("Side by Side Comparison Graph") + graph.addPass(imageLoaderA, "ImageLoaderA") + graph.addPass(imageLoaderB, "ImageLoaderB") + graph.addPass(sideComparison, "SideBySidePass") + + graph.addEdge("ImageLoaderA.dst", "SideBySidePass.leftInput") + graph.addEdge("ImageLoaderB.dst", "SideBySidePass.rightInput") + graph.markOutput("SideBySidePass.output") + + return graph + +side_comparison_graph = test_side_comparison() + +m.addGraph(side_comparison_graph) diff --git a/Samples/Effects/Shadows/Data/VisualizeMap.slang b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp similarity index 72% rename from Samples/Effects/Shadows/Data/VisualizeMap.slang rename to Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp index 0744ce8bc..2896f492f 100644 --- a/Samples/Effects/Shadows/Data/VisualizeMap.slang +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,30 +25,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -SamplerState gSampler : register(s0); +#include "SplitScreenPass.h" -#ifdef _USE_2D_ARRAY -Texture2DArray gTexture; -#else -Texture2D gTexture; -#endif - -cbuffer PerImageCB : register(b0) +namespace { - int cascade; -}; + // Where is our shader located? + const std::string kSplitShader = "Data\\SplitScreen.ps.slang"; +} + +SplitScreenPass::SplitScreenPass() { createProgram(); } -float4 calcColor(float2 texC) +SplitScreenPass::SharedPtr SplitScreenPass::create(RenderContext* pRenderContext, const Dictionary& dict) { -#ifdef _USE_2D_ARRAY - float d = gTexture.SampleLevel(gSampler, float3(texC, float(cascade)), 0).r; -#else - float d = gTexture.SampleLevel(gSampler, texC, 0).r; -#endif - return float4(d.xxx, 1); + SharedPtr pPass = SharedPtr(new SplitScreenPass()); + pPass->parseDictionary(dict); + return pPass; } -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 +void SplitScreenPass::createProgram() { - return calcColor(texC); + // Create our shader that splits the screen. + mpSplitShader = FullScreenPass::create(kSplitShader); } diff --git a/Framework/Source/Utils/ThreadPool.h b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h similarity index 75% rename from Framework/Source/Utils/ThreadPool.h rename to Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h index c272c8362..47e4903e6 100644 --- a/Framework/Source/Utils/ThreadPool.h +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,29 +25,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include +#pragma once +#include "Falcor.h" +#include "../ComparisonPass.h" -template -class ThreadPool +using namespace Falcor; + +class SplitScreenPass : public ComparisonPass { public: - ~ThreadPool() - { - for (auto& t : mThreads) - { - if (t.joinable()) t.join(); - } - } + using SharedPtr = std::shared_ptr; - std::thread& getAvailable() - { - std::thread& t = mThreads[mCurrent]; - if (t.joinable()) t.join(); - mCurrent = (mCurrent + 1) % arraysize(mThreads); - return t; - } + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + virtual std::string getDesc() override { return "Allows the user to split the screen between two inputs."; } private: - std::thread mThreads[threadCount]; - uint32_t mCurrent = 0; -}; \ No newline at end of file + SplitScreenPass(); + virtual void createProgram() override; +}; diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/Testing/testSplitScreen.py b/Source/RenderPasses/DebugPasses/SplitScreenPass/Testing/testSplitScreen.py new file mode 100644 index 000000000..fc04e0e26 --- /dev/null +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/Testing/testSplitScreen.py @@ -0,0 +1,20 @@ +def test_split_screen(): + loadRenderPassLibrary("DebugPasses.dll") + imageLoaderA = RenderPass("ImageLoader", {'filename': 'Cubemaps\\Sorsele3\\posz.jpg', 'mips': False, 'srgb': False}) + imageLoaderB = RenderPass("ImageLoader", {'filename': 'Cubemaps\\Sorsele3\\posz.jpg', 'mips': False, 'srgb': True}) + splitScreen = RenderPass("SplitScreenPass") + + graph = RenderGraph("Split Screen Graph") + graph.addPass(imageLoaderA, "ImageLoaderA") + graph.addPass(imageLoaderB, "ImageLoaderB") + graph.addPass(splitScreen, "SplitScreenPass") + + graph.addEdge("ImageLoaderA.dst", "SplitScreenPass.leftInput") + graph.addEdge("ImageLoaderB.dst", "SplitScreenPass.rightInput") + graph.markOutput("SplitScreenPass.output") + + return graph + +split_screen_graph = test_split_screen() + +m.addGraph(split_screen_graph) diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp new file mode 100644 index 000000000..414a6657f --- /dev/null +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp @@ -0,0 +1,402 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "ErrorMeasurePass.h" +#include + +namespace +{ + const char kErrorComputationShaderFile[] = "RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang"; + const char kConstantBufferName[] = "PerFrameCB"; + + // Input channels + const char kInputChannelWorldPosition[] = "WorldPosition"; + const char kInputChannelSourceImage[] = "Source"; + const char kInputChannelReferenceImage[] = "Reference"; + + // Output channel + const char kOutputChannelImage[] = "Output"; + + // Serialized parameters + const char kReferenceImagePath[] = "ReferenceImagePath"; + const char kMeasurementsFilePath[] = "MeasurementsFilePath"; + const char kIgnoreBackground[] = "IgnoreBackground"; + const char kComputeSquaredDifference[] = "ComputeSquaredDifference"; + const char kUseLoadedReference[] = "UseLoadedReference"; + const char kReportRunningError[] = "ReportRunningError"; + const char kRunningErrorSigma[] = "RunningErrorSigma"; + const char kSelectedOutputId[] = "SelectedOutputId"; +} + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("ErrorMeasurePass", "Error Measurement Pass", ErrorMeasurePass::create); +} + +const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtons = +{ + { OutputId::source, "source", true }, + { OutputId::reference, "reference", true }, + { OutputId::difference, "difference", true } +}; +const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtonsSourceOnly = +{ + { OutputId::source, "source", true } +}; + +ErrorMeasurePass::SharedPtr ErrorMeasurePass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + ErrorMeasurePass::SharedPtr pass = SharedPtr(new ErrorMeasurePass); + return pass->init(pRenderContext, dict) ? pass : nullptr; +} + +bool ErrorMeasurePass::init(RenderContext* pRenderContext, const Dictionary& dict) +{ + for (const auto& v : dict) + { + if (v.key() == kReferenceImagePath) mReferenceImagePath = (const std::string &)v.val(); + else if (v.key() == kMeasurementsFilePath) mMeasurementsFilePath = (const std::string &)v.val(); + else if (v.key() == kIgnoreBackground) mIgnoreBackground = v.val(); + else if (v.key() == kComputeSquaredDifference) mComputeSquaredDifference = v.val(); + else if (v.key() == kUseLoadedReference) mUseLoadedReference = v.val(); + else if (v.key() == kReportRunningError) mReportRunningError = v.val(); + else if (v.key() == kRunningErrorSigma) mRunningErrorSigma = v.val(); + else if (v.key() == kSelectedOutputId) mSelectedOutputId = v.val(); + else + { + logWarning("Unknown field `" + v.key() + "` in ErrorMeasurePass dictionary"); + } + } + + // Load/create files (if specified in config). + loadReference(); + openMeasurementsFile(); + + mpParallelReduction = ComputeParallelReduction::create(); + if (!mpParallelReduction) return false; + + mpErrorMeasurerPass = ComputePass::create(kErrorComputationShaderFile); + if (!mpErrorMeasurerPass) return false; + + return true; +} + +Dictionary ErrorMeasurePass::getScriptingDictionary() +{ + Dictionary dict; + dict[kReferenceImagePath] = mReferenceImagePath; + dict[kMeasurementsFilePath] = mMeasurementsFilePath; + dict[kIgnoreBackground] = mIgnoreBackground; + dict[kComputeSquaredDifference] = mComputeSquaredDifference; + dict[kUseLoadedReference] = mUseLoadedReference; + dict[kReportRunningError] = mReportRunningError; + dict[kRunningErrorSigma] = mRunningErrorSigma; + dict[kSelectedOutputId] = mSelectedOutputId; + return dict; +} + +RenderPassReflection ErrorMeasurePass::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + reflector.addInput(kInputChannelSourceImage, "Source image"); + reflector.addInput(kInputChannelReferenceImage, "Reference image (optional)").flags(RenderPassReflection::Field::Flags::Optional); + reflector.addInput(kInputChannelWorldPosition, "World-space position").flags(RenderPassReflection::Field::Flags::Optional); + // TODO: when compile() is available, match the format of the source image? + reflector.addOutput(kOutputChannelImage, "Output image").format(ResourceFormat::RGBA32Float); + return reflector; +} + +void ErrorMeasurePass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + Texture::SharedPtr pSourceImageTexture = renderData[kInputChannelSourceImage]->asTexture(); + Texture::SharedPtr pOutputImageTexture = renderData[kOutputChannelImage]->asTexture(); + + // Create the texture for the difference image if this is our first + // time through or if the source image resolution has changed. + const uint32_t width = pSourceImageTexture->getWidth(), height = pSourceImageTexture->getHeight(); + if (!mpDifferenceTexture || mpDifferenceTexture->getWidth() != width || + mpDifferenceTexture->getHeight() != height) + { + mpDifferenceTexture = Texture::create2D(width, height, ResourceFormat::RGBA32Float, 1, 1, nullptr, + Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + assert(mpDifferenceTexture); + } + + mMeasurements.valid = false; + + Texture::SharedPtr pReference = getReference(renderData); + if (!pReference) + { + // We don't have a reference image, so just copy the source image to the output. + pRenderContext->blit(pSourceImageTexture->getSRV(), pOutputImageTexture->getRTV()); + return; + } + + runDifferencePass(pRenderContext, renderData); + runReductionPasses(pRenderContext, renderData); + + switch (mSelectedOutputId) + { + case OutputId::source: + pRenderContext->blit(pSourceImageTexture->getSRV(), pOutputImageTexture->getRTV()); + break; + case OutputId::reference: + pRenderContext->blit(pReference->getSRV(), pOutputImageTexture->getRTV()); + break; + case OutputId::difference: + pRenderContext->blit(mpDifferenceTexture->getSRV(), pOutputImageTexture->getRTV()); + break; + default: + throw std::exception("Unhandled OutputId case in ErrorMeasurePass"); + } + + saveMeasurementsToFile(); +} + +void ErrorMeasurePass::runDifferencePass(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Bind textures. + Texture::SharedPtr pSourceTexture = renderData[kInputChannelSourceImage]->asTexture(); + Texture::SharedPtr pWorldPositionTexture = renderData[kInputChannelWorldPosition]->asTexture(); + mpErrorMeasurerPass["gReference"] = getReference(renderData); + mpErrorMeasurerPass["gSource"] = pSourceTexture; + mpErrorMeasurerPass["gWorldPosition"] = pWorldPositionTexture; + mpErrorMeasurerPass["gResult"] = mpDifferenceTexture; + + // Set constant buffer parameters. + const glm::uvec2 resolution = glm::uvec2(pSourceTexture->getWidth(), pSourceTexture->getHeight()); + mpErrorMeasurerPass[kConstantBufferName]["gResolution"] = resolution; + // If the world position texture is unbound, then don't do the background pixel check. + mpErrorMeasurerPass[kConstantBufferName]["gIgnoreBackground"] = (uint32_t)(mIgnoreBackground && pWorldPositionTexture); + mpErrorMeasurerPass[kConstantBufferName]["gComputeDiffSqr"] = (uint32_t)mComputeSquaredDifference; + + // Run the compute shader. + mpErrorMeasurerPass->execute(pRenderContext, resolution.x, resolution.y); +} + +void ErrorMeasurePass::runReductionPasses(RenderContext* pRenderContext, const RenderData& renderData) +{ + glm::vec4 error; + if (!mpParallelReduction->execute(pRenderContext, mpDifferenceTexture, ComputeParallelReduction::Type::Sum, &error)) + { + throw std::exception("Error running parallel reduction in ErrorMeasurePass"); + } + + const float pixelCountf = static_cast(mpDifferenceTexture->getWidth() * mpDifferenceTexture->getHeight()); + mMeasurements.error = error / pixelCountf; + mMeasurements.avgError = (mMeasurements.error.x + mMeasurements.error.y + mMeasurements.error.z) / 3.f; + mMeasurements.valid = true; + + if (mRunningAvgError < 0) + { + // The running error values are invalid. Start them off with the current frame's error. + mRunningError = mMeasurements.error; + mRunningAvgError = mMeasurements.avgError; + } + else + { + mRunningError = mRunningErrorSigma * mRunningError + (1 - mRunningErrorSigma) * mMeasurements.error; + mRunningAvgError = mRunningErrorSigma * mRunningAvgError + (1 - mRunningErrorSigma) * mMeasurements.avgError; + } +} + +void ErrorMeasurePass::renderUI(Gui::Widgets& widget) +{ + const auto getFilename = [](const std::string& path) + { + return path.empty() ? "N/A" : getFilenameFromPath(path); + }; + + // Create a button for loading the reference image. + if (widget.button("Load reference")) + { + FileDialogFilterVec filters; + filters.push_back({ "exr", "High Dynamic Range" }); + filters.push_back({ "pfm", "Portable Float Map" }); + std::string filename; + if (openFileDialog(filters, filename)) + { + mReferenceImagePath = filename; + loadReference(); + } + } + + // Create a button for defining the measurements output file. + if (widget.button("Set output data file", true)) + { + FileDialogFilterVec filters; + filters.push_back({ "csv", "CSV Files" }); + std::string filename; + if (saveFileDialog(filters, filename)) + { + mMeasurementsFilePath = filename; + openMeasurementsFile(); + } + } + + // Radio buttons to select the output. + widget.text("Show:"); + if (mMeasurements.valid) + { + widget.radioButtons(sOutputSelectionButtons, mSelectedOutputId); + widget.tooltip("Press 'O' to change output mode; hold 'Shift' to reverse the cycling.\n\n" + "Note: Difference is computed based on current - reference value.", true); + } + else + { + uint32_t dummyId = 0; + widget.radioButtons(sOutputSelectionButtonsSourceOnly, dummyId); + } + + widget.checkbox("Ignore background", mIgnoreBackground); + widget.tooltip(("Do not include background pixels in the error measurements.\n" + "This option requires the optional input '" + std::string(kInputChannelWorldPosition) + "' to be bound").c_str(), true); + widget.checkbox("Compute L2 error (rather than L1)", mComputeSquaredDifference); + + widget.checkbox("Use loaded reference image", mUseLoadedReference); + widget.tooltip("Take the reference from the loaded image instead or the input channel.\n\n" + "If the chosen reference doesn't exist, the error measurements are disabled.", true); + // Display the filename of the reference file. + const std::string referenceText = "Reference: " + getFilename(mReferenceImagePath); + widget.text(referenceText.c_str()); + if (!mReferenceImagePath.empty()) + { + widget.tooltip(mReferenceImagePath.c_str()); + } + + // Display the filename of the measurement file. + const std::string outputText = "Output: " + getFilename(mMeasurementsFilePath); + widget.text(outputText.c_str()); + if (!mMeasurementsFilePath.empty()) + { + widget.tooltip(mMeasurementsFilePath.c_str()); + } + + // Print numerical error (scalar and RGB). + if (widget.checkbox("Report running error", mReportRunningError) && mReportRunningError) + { + // The checkbox was enabled; mark the running error values invalid so that they start fresh. + mRunningAvgError = -1.f; + } + widget.tooltip(("Exponential moving average, sigma = " + std::to_string(mRunningErrorSigma)).c_str()); + if (mMeasurements.valid) + { + // Use stream so we can control formatting. + std::ostringstream oss; + oss << std::scientific; + oss << (mComputeSquaredDifference ? "MSE (avg): " : "L1 error (avg): ") << + (mReportRunningError ? mRunningAvgError : mMeasurements.avgError) << std::endl; + oss << (mComputeSquaredDifference ? "MSE (rgb): " : "L1 error (rgb): ") << + (mReportRunningError ? mRunningError.r : mMeasurements.error.r) << ", " << + (mReportRunningError ? mRunningError.g : mMeasurements.error.g) << ", " << + (mReportRunningError ? mRunningError.b : mMeasurements.error.b); + widget.text(oss.str().c_str()); + } + else + { + widget.text("Error: N/A"); + } +} + +bool ErrorMeasurePass::onKeyEvent(const KeyboardEvent& keyEvent) +{ + if (keyEvent.type == KeyboardEvent::Type::KeyPressed && keyEvent.key == KeyboardEvent::Key::O) + { + if (keyEvent.mods.isShiftDown) + { + mSelectedOutputId = (mSelectedOutputId - 1 + OutputId::NUM_OUTPUTS) % OutputId::NUM_OUTPUTS; + } + else + { + mSelectedOutputId = (mSelectedOutputId + 1) % OutputId::NUM_OUTPUTS; + } + return true; + } + + return false; +} + +void ErrorMeasurePass::loadReference() +{ + if (mReferenceImagePath.empty()) return; + + // TODO: it would be nice to also be able to take the reference image as an input. + mpReferenceTexture = Texture::createFromFile(mReferenceImagePath, false /* no MIPs */, false /* linear color */); + if (!mpReferenceTexture) + { + logError("Failed to load texture " + mReferenceImagePath); + mReferenceImagePath = ""; + } + + mUseLoadedReference = mpReferenceTexture != nullptr; + mRunningAvgError = -1.f; // Mark running error values as invalid. +} + +Texture::SharedPtr ErrorMeasurePass::getReference(const RenderData& renderData) const +{ + return mUseLoadedReference ? mpReferenceTexture : renderData[kInputChannelReferenceImage]->asTexture(); +} + +void ErrorMeasurePass::openMeasurementsFile() +{ + if (mMeasurementsFilePath.empty()) return; + + mMeasurementsFile = std::ofstream(mMeasurementsFilePath, std::ios::trunc); + if (!mMeasurementsFile) + { + logError("Failed to open file " + mMeasurementsFilePath); + mMeasurementsFilePath = ""; + } + else + { + if (mComputeSquaredDifference) + { + mMeasurementsFile << "avg_L2_error,red_L2_error,green_L2_error,blue_L2_error" << std::endl; + } + else + { + mMeasurementsFile << "avg_L1_error,red_L1_error,green_L1_error,blue_L1_error" << std::endl; + } + mMeasurementsFile << std::scientific; + } +} + +void ErrorMeasurePass::saveMeasurementsToFile() +{ + if (!mMeasurementsFile) return; + + assert(mMeasurements.valid); + mMeasurementsFile << mMeasurements.avgError << ","; + mMeasurementsFile << mMeasurements.error.r << ',' << mMeasurements.error.g << ',' << mMeasurements.error.b; + mMeasurementsFile << std::endl; +} diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h new file mode 100644 index 000000000..831fe85da --- /dev/null +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h @@ -0,0 +1,102 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "Utils/Algorithm/ComputeParallelReduction.h" + +using namespace Falcor; + +class ErrorMeasurePass : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return "Measures error with respect to a reference image"; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; + +private: + ErrorMeasurePass() = default; + + bool init(RenderContext* pRenderContext, const Dictionary& dict); + + void loadReference(); + Texture::SharedPtr getReference(const RenderData& renderData) const; + void openMeasurementsFile(); + void saveMeasurementsToFile(); + + void runDifferencePass(RenderContext* pRenderContext, const RenderData& renderData); + void runReductionPasses(RenderContext* pRenderContext, const RenderData& renderData); + + ComputePass::SharedPtr mpErrorMeasurerPass; + ComputeParallelReduction::SharedPtr mpParallelReduction; + + struct + { + float3 error; ///< Error (either L1 or MSE) in RGB. + float avgError; ///< Error averaged over color components. + bool valid = false; + } mMeasurements; + + // Internal state + float3 mRunningError = float3(0.f, 0.f, 0.f); + float mRunningAvgError = -1.f; ///< A negative value indicates that both running error values are invalid. + + Texture::SharedPtr mpReferenceTexture; + Texture::SharedPtr mpDifferenceTexture; + + std::ofstream mMeasurementsFile; + + // UI variables + std::string mReferenceImagePath; ///< Path to the reference used in the comparison. + std::string mMeasurementsFilePath; ///< Path to the output file where measurements are stored (.csv). + + bool mIgnoreBackground = true; ///< If true, do not measure error on pixels that belong to the background. + bool mComputeSquaredDifference = true; + bool mUseLoadedReference = false; ///< If true, use loaded reference image instead of input. + bool mReportRunningError = true; + float mRunningErrorSigma = 0.995f; + + enum OutputId + { + source = 0, + reference, + difference, + NUM_OUTPUTS + }; + + uint32_t mSelectedOutputId = OutputId::source; + + static const Gui::RadioButtonGroup sOutputSelectionButtons; + static const Gui::RadioButtonGroup sOutputSelectionButtonsSourceOnly; +}; diff --git a/Samples/Core/StereoRendering/StereoRendering.vcxproj b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.vcxproj similarity index 69% rename from Samples/Core/StereoRendering/StereoRendering.vcxproj rename to Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.vcxproj index 0a718cf96..dc0f9d5ef 100644 --- a/Samples/Core/StereoRendering/StereoRendering.vcxproj +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.vcxproj @@ -1,4 +1,4 @@ - + @@ -10,59 +10,52 @@ x64 + + {DAE949BD-2C44-40DA-A592-7CCAB00D4A7D} + Win32Proj + ErrorMeasurePass + 10.0.17763.0 + ErrorMeasurePass + + + + + + + + - + - + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} - - - - - Document - + - - - Document - - - - {7C6C43DE-EEF4-4165-BE92-ED753D3799EE} - Win32Proj - StereoRendering - 10.0.17763.0 - - - Application + DynamicLibrary true v141 Unicode + Data\RenderPasses\$(ProjectName) - Application + DynamicLibrary false v141 true Unicode + Data\RenderPasses\$(ProjectName) - - - - - - true @@ -76,7 +69,10 @@ Level3 Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows @@ -91,7 +87,10 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang new file mode 100644 index 000000000..d11427dd3 --- /dev/null +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang @@ -0,0 +1,40 @@ +/*************************************************************************** + # Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +Texture2D gReference; +Texture2D gSource; +Texture2D gWorldPosition; +RWTexture2D gResult; + +cbuffer PerFrameCB +{ + uint2 gResolution; + uint gIgnoreBackground; + uint gComputeDiffSqr; +}; + +[numthreads(16, 16, 1)] +void main( uint3 DTid : SV_DispatchThreadID ) +{ + const uint2 pixel = DTid.xy; + if (any(pixel >= gResolution)) return; + + // Determine if we should include this pixel or not. + const float4 worldPos = gWorldPosition[pixel]; + const bool isForeground = worldPos.w != 0.0f; // We're using the w-component to identify valid pixels. + + const bool isPixelValid = !gIgnoreBackground || isForeground; + + // Compute error based on the current options. + float3 diff = isPixelValid ? abs(gSource[pixel].rgb - gReference[pixel].rgb) : float3(0.0f); + if (gComputeDiffSqr) diff *= diff; + + gResult[pixel] = float4(diff, 0.0f); +} diff --git a/Source/RenderPasses/GBuffer/GBuffer.cpp b/Source/RenderPasses/GBuffer/GBuffer.cpp new file mode 100644 index 000000000..1970ada16 --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBuffer.cpp @@ -0,0 +1,208 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "GBuffer.h" +#include "GBufferRaster.h" +#include "GBufferRT.h" + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +static void regGBufferPass(Falcor::ScriptBindings::Module& m) +{ + auto lodeModeBinding = m.enum_("LODMode"); + lodeModeBinding.regEnumVal(GBufferRT::LODMode::UseMip0); + lodeModeBinding.regEnumVal(GBufferRT::LODMode::RayDifferentials); + // lodeModeBinding.regEnumVal(GBufferRT::LODMode::TexLODCone) not implemented + + auto samplePatternEnum = m.enum_("SamplePattern"); + samplePatternEnum.regEnumVal(GBuffer::SamplePattern::Center); + samplePatternEnum.regEnumVal(GBuffer::SamplePattern::DirectX); + samplePatternEnum.regEnumVal(GBuffer::SamplePattern::Halton); + samplePatternEnum.regEnumVal(GBuffer::SamplePattern::Stratified); +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("GBufferRaster", "Raster-Based GBuffer Creation", GBufferRaster::create); + lib.registerClass("GBufferRT", "Ray Tracing-Based GBuffer Creation", GBufferRT::create); + Falcor::ScriptBindings::registerBinding(regGBufferPass); +} + +// Note that channel order should correspond to SV_TARGET index order used in +// GBufferRaster's primary fragment shader. +const ChannelList GBuffer::kGBufferChannels = +{ + { "posW", "gPosW", "world space position", true /* optional */, ResourceFormat::RGBA32Float }, + { "normW", "gNormW", "world space normal", true /* optional */, ResourceFormat::RGBA32Float }, + { "bitangentW", "gBitangentW", "world space bitangent", true /* optional */, ResourceFormat::RGBA32Float }, + { "texC", "gTexC", "texture coordinates", true /* optional */, ResourceFormat::RGBA32Float }, + { "diffuseOpacity", "gDiffuseOpacity", "diffuse color and opacity", true /* optional */, ResourceFormat::RGBA32Float }, + { "specRough", "gSpecRough", "specular color and roughness", true /* optional */, ResourceFormat::RGBA32Float }, + { "emissive", "gEmissive", "emissive color", true /* optional */, ResourceFormat::RGBA32Float }, + { "matlExtra", "gMatlExtra", "additional material data", true /* optional */, ResourceFormat::RGBA32Float }, +}; + +namespace +{ + // Serialized parameters + const char kForceCullMode[] = "forceCullMode"; + const char kCullMode[] = "cull"; + const char kSamplePattern[] = "samplePattern"; + const char kSampleCount[] = "sampleCount"; + + // UI variables + const Gui::DropdownList kCullModeList = + { + { (uint32_t)RasterizerState::CullMode::None, "None" }, + { (uint32_t)RasterizerState::CullMode::Back, "Back" }, + { (uint32_t)RasterizerState::CullMode::Front, "Front" }, + }; + + const Gui::DropdownList kSamplePatternList = + { + { (uint32_t)GBuffer::SamplePattern::Center, "Center" }, + { (uint32_t)GBuffer::SamplePattern::DirectX, "DirectX" }, + { (uint32_t)GBuffer::SamplePattern::Halton, "Halton" }, + { (uint32_t)GBuffer::SamplePattern::Stratified, "Stratified" }, + }; +} + +GBuffer::GBuffer() : mGBufferParams{} +{ +} + +bool GBuffer::parseDictionary(const Dictionary& dict) +{ + for (const auto& v : dict) + { + if (v.key() == kForceCullMode) mForceCullMode = v.val(); + else if (v.key() == kCullMode) setCullMode((RasterizerState::CullMode)v.val()); + else if (v.key() == kSamplePattern) mSamplePattern = (SamplePattern)v.val(); + else if (v.key() == kSampleCount) mSampleCount = v.val(); + else + { + logWarning("Unknown field `" + v.key() + "` in a GBuffer dictionary"); + } + } + return true; +} + +Dictionary GBuffer::getScriptingDictionary() +{ + Dictionary dict; + dict[kForceCullMode] = mForceCullMode; + dict[kCullMode] = mCullMode; + dict[kSamplePattern] = mSamplePattern; + dict[kSampleCount] = mSampleCount; + return dict; +} + +void GBuffer::renderUI(Gui::Widgets& widget) +{ + // Cull mode controls. + mOptionsChanged |= widget.checkbox("Force cull mode", mForceCullMode); + widget.tooltip("Enable this option to force the same cull mode for all geometry.\n\n" + "Otherwise the default for rasterization is to set the cull mode automatically based on triangle winding, and for ray tracing to disable culling.", true); + + if (mForceCullMode) + { + uint32_t cullMode = (uint32_t)mCullMode; + if (widget.dropdown("Cull mode", kCullModeList, cullMode)) + { + setCullMode((RasterizerState::CullMode)cullMode); + mOptionsChanged = true; + } + } + + // Sample pattern controls. + bool updatePattern = widget.dropdown("Sample pattern", kSamplePatternList, (uint32_t&)mSamplePattern); + widget.tooltip("Selects sample pattern for anti-aliasing over multiple frames.\n\n" + "The camera jitter is set at the start of each frame based on the chosen pattern. All render passes should see the same jitter.\n" + "'Center' disables anti-aliasing by always sampling at the center of the pixel.", true); + if (mSamplePattern != SamplePattern::Center) + { + updatePattern |= widget.var("Sample count", mSampleCount, 1u); + widget.tooltip("Number of samples in the anti-aliasing sample pattern.", true); + } + if (updatePattern) + { + updateSamplePattern(); + mOptionsChanged = true; + } +} + +void GBuffer::compile(RenderContext* pContext, const CompileData& compileData) +{ + mGBufferParams.frameSize = vec2(compileData.defaultTexDims); + mGBufferParams.invFrameSize = 1.f / mGBufferParams.frameSize; + + if (mpScene) + { + auto pCamera = mpScene->getCamera(); + pCamera->setPatternGenerator(pCamera->getPatternGenerator(), mGBufferParams.invFrameSize); + } + return; +} + +void GBuffer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + mpScene = pScene; + mGBufferParams.frameCount = 0; + updateSamplePattern(); +} + +void GBuffer::updateSamplePattern() +{ + if (mpScene) + { + auto pGen = createSamplePattern(mSamplePattern, mSampleCount); + if (pGen) mSampleCount = pGen->getSampleCount(); + mpScene->getCamera()->setPatternGenerator(pGen, mGBufferParams.invFrameSize); + } +} + +CPUSampleGenerator::SharedPtr GBuffer::createSamplePattern(SamplePattern type, uint32_t sampleCount) +{ + switch (type) + { + case SamplePattern::Center: + return nullptr; + case SamplePattern::DirectX: + return DxSamplePattern::create(sampleCount); + case SamplePattern::Halton: + return HaltonSamplePattern::create(sampleCount); + case SamplePattern::Stratified: + return StratifiedSamplePattern::create(sampleCount); + default: + should_not_get_here(); + return nullptr; + } +} diff --git a/Source/RenderPasses/GBuffer/GBuffer.h b/Source/RenderPasses/GBuffer/GBuffer.h new file mode 100644 index 000000000..00e2bf3bd --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBuffer.h @@ -0,0 +1,89 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "GBufferParams.h" +#include "RenderGraph/RenderPassHelpers.h" + +using namespace Falcor; + +/** Base class for the different G-buffer passes. +*/ +class GBuffer : public RenderPass, inherit_shared_from_this +{ +public: + enum class SamplePattern : uint32_t + { + Center, + DirectX, + Halton, + Stratified, + }; + + virtual void renderUI(Gui::Widgets& widget) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual Dictionary getScriptingDictionary() override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + +protected: + GBuffer(); + virtual bool parseDictionary(const Dictionary& dict); + virtual void setCullMode(RasterizerState::CullMode mode) { mCullMode = mode; } + void updateSamplePattern(); + static CPUSampleGenerator::SharedPtr createSamplePattern(SamplePattern type, uint32_t sampleCount); + + // Constants used in derived classes + static const ChannelList kGBufferChannels; + + // Internal state + Scene::SharedPtr mpScene; + GBufferParams mGBufferParams; + + // UI variables + bool mForceCullMode = false; ///< Force cull mode for all geometry, otherwise set it based on the scene. + RasterizerState::CullMode mCullMode = RasterizerState::CullMode::Back; ///< Cull mode to use for when mForceCullMode is true. + SamplePattern mSamplePattern = SamplePattern::Center; ///< Which camera jitter sample pattern to use. + uint32_t mSampleCount = 16; ///< Sample count for camera jitter. + bool mOptionsChanged = false; +}; + +#define str(a) case GBuffer::SamplePattern::a: return #a +inline std::string to_string(GBuffer::SamplePattern type) +{ + switch (type) + { + str(Center); + str(DirectX); + str(Halton); + str(Stratified); + default: + should_not_get_here(); + return ""; + } +} +#undef str diff --git a/Source/RenderPasses/GBuffer/GBuffer.vcxproj b/Source/RenderPasses/GBuffer/GBuffer.vcxproj new file mode 100644 index 000000000..15822296a --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBuffer.vcxproj @@ -0,0 +1,112 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {CA155B01-7528-4D81-B5CD-E801B4AA4027} + Win32Proj + GBuffer + 10.0.17763.0 + GBuffer + + + + + + + + + + + + + + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + + + + + + + + + + + + + + DynamicLibrary + true + v141 + Unicode + Data\RenderPasses\$(ProjectName) + + + DynamicLibrary + false + v141 + true + Unicode + Data\RenderPasses\$(ProjectName) + + + + + + + true + + + false + + + + + + Level3 + Disabled + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/Source/RenderPasses/GBuffer/GBuffer.vcxproj.filters b/Source/RenderPasses/GBuffer/GBuffer.vcxproj.filters new file mode 100644 index 000000000..d1283241e --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBuffer.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Effects/HDRToneMapping/Data/HDRToneMapping.hlsl b/Source/RenderPasses/GBuffer/GBufferHelpers.slang similarity index 55% rename from Samples/Effects/HDRToneMapping/Data/HDRToneMapping.hlsl rename to Source/RenderPasses/GBuffer/GBufferHelpers.slang index bf6186b9a..54207ab4a 100644 --- a/Samples/Effects/HDRToneMapping/Data/HDRToneMapping.hlsl +++ b/Source/RenderPasses/GBuffer/GBufferHelpers.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,57 +25,54 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#define PI 3.141591 -Texture2D gEnvMap; -SamplerState gSampler; +/** Helper functions for generating the G-buffer. +*/ -cbuffer PerFrameCB : register(b0) -{ - float4x4 gWvpMat; - float4x4 gWorldMat; - float3 gEyePosW; - float gLightIntensity; - float gSurfaceRoughness; -}; +#include "GBufferParams.h" -struct VsIn +import Utils.Math.MathHelpers; +__exported import Experimental.Scene.Material.MaterialHelpers; + +struct GBuffer { - float4 pos : POSITION; - float3 normal : NORMAL; + float4 posW; + float4 normW; + float4 bitangentW; + float4 texC; + float4 diffuseOpacity; + float4 specRough; + float4 emissive; + float4 matlExtra; }; -struct VsOut +shared cbuffer PerFrameCB { - float4 pos : SV_POSITION; - float3 posW : POSITION; - float3 normalW : NORMAL; + GBufferParams gParams; }; -VsOut vs(VsIn vIn) -{ - VsOut vOut; - vOut.pos = (mul(vIn.pos, gWvpMat)); - vOut.posW = (mul(vIn.pos, gWorldMat)).xyz; - vOut.normalW = (mul(float4(vIn.normal, 0), gWorldMat)).xyz; - return vOut; -} +static const uint kInvalidIndex = 0xffffffff; -float4 ps(VsOut vOut) : SV_TARGET +/** Helper function to store G-buffer channels. +*/ +GBuffer storeGBufferOutput(ShadingData sd) { - float3 p = normalize(vOut.normalW); - float2 uv; - uv.x = (1 + atan2(-p.z, p.x) / PI) * 0.5; - uv.y = 1 - (-acos(p.y) / PI); - float4 color = gEnvMap.Sample(gSampler, uv); - color.rgb *= gLightIntensity; + GBuffer gbuf; + + // Check that tangent space exists, otherwise create one based on the normal. + // Note that this check also catches NaNs, should they occur. + float3 bitangent = dot(sd.B, sd.B) > 0.f ? sd.B : perp_stark(sd.N); + + gbuf.posW = float4(sd.posW, 1.f); + gbuf.normW = float4(sd.N, 0.f); + gbuf.bitangentW = float4(bitangent, 0.f); + gbuf.texC = float4(sd.uv, 0.f, 0.f); - // compute halfway vector - float3 eyeDir = normalize(gEyePosW - vOut.posW); - float3 h = normalize(eyeDir + vOut.normalW); - float edoth = dot(eyeDir, h); - float intensity = pow(clamp(edoth, 0, 1), gSurfaceRoughness); + MaterialParams matParams = getMaterialParams(sd); + gbuf.diffuseOpacity = matParams.diffuseOpacity; + gbuf.specRough = matParams.specularRoughness; + gbuf.emissive = matParams.emissive; + gbuf.matlExtra = matParams.extraParams; - color.rgb *= intensity; - return color; + return gbuf; } diff --git a/Framework/Source/Graphics/Model/ModelRenderer.h b/Source/RenderPasses/GBuffer/GBufferParams.h similarity index 69% rename from Framework/Source/Graphics/Model/ModelRenderer.h rename to Source/RenderPasses/GBuffer/GBufferParams.h index f50f17d4d..0c10b68a5 100644 --- a/Framework/Source/Graphics/Model/ModelRenderer.h +++ b/Source/RenderPasses/GBuffer/GBufferParams.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,25 +26,22 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Graphics/Model/Model.h" +#include "Data/HostDeviceData.h" -namespace Falcor -{ - class RenderContext; - class Camera; +BEGIN_NAMESPACE_FALCOR - deprecate("3.3", "") - class ModelRenderer - { - public: - /** Render a model. Uses frustum culling, if enabled. - \param[in] pRenderContext The render context - \param[in] pModel The model to render - \param[in] pCamera The camera to use - \param[in] frustumCulling Enable/disable per-mesh frustum culling - */ - static void render(RenderContext* pRenderContext, Model::SharedPtr pModel, Camera* pCamera, bool frustumCulling = true); +/** Constants shared between host and device. + Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. + Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. + https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx +*/ +struct GBufferParams +{ + float2 frameSize; ///< Frame buffer dimension in pixels. + float2 invFrameSize; ///< 1.0 / frameSize. + uint frameCount; + uint rayFlags; ///< Ray flags for TraceRay(). See D3D12_RAY_FLAG_* in d3d12.h. + int2 _pad0; +}; - private: - }; -} +END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/GBuffer/GBufferRT.cpp b/Source/RenderPasses/GBuffer/GBufferRT.cpp new file mode 100644 index 000000000..4d874ae8c --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBufferRT.cpp @@ -0,0 +1,213 @@ +/*************************************************************************** + # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. + #/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Falcor.h" +#include "RenderGraph/RenderPassStandardFlags.h" +#include "GBufferRT.h" + +namespace +{ + const char* kFileRayTrace = "RenderPasses/GBuffer/RaytracePrimary.rt.slang"; + + const char* kEntryPointRayGen = "rayGen"; + const char* kEntryPointMiss0 = "primaryMiss"; + const char* kEntryPrimaryAnyHit = "primaryAnyHit"; + const char* kEntryPrimaryClosestHit = "primaryClosestHit"; + + // Serialized parameters + const std::string kLOD = "texLOD"; + + const Falcor::Gui::DropdownList kLODModeList = + { + { (uint32_t)GBufferRT::LODMode::UseMip0, "Mip0" }, + { (uint32_t)GBufferRT::LODMode::RayDifferentials, "Ray Diff" }, + //{ (uint32_t)GBufferRT::LODMode::TexLODCone, "Tex LOD cone" }, // Not implemented + }; + + // Additional output channels. + const ChannelList kGBufferRTChannels = + { + { "faceNormalW", "gFaceNormalW", "Face normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, + { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. + { "visBuffer", "gVisBuffer", "Visibility buffer", true /* optional */, ResourceFormat::RGBA32Uint }, + }; +}; + +RenderPassReflection GBufferRT::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + + // Add all outputs as UAVs. + auto addOutput = [&](const ChannelDesc& output) + { + auto& f = r.addOutput(output.name, output.desc).format(output.format).bindFlags(Resource::BindFlags::UnorderedAccess); + if (output.optional) f.flags(RenderPassReflection::Field::Flags::Optional); + }; + for (auto it : kGBufferChannels) addOutput(it); + for (auto it : kGBufferRTChannels) addOutput(it); + + return r; +} + +bool GBufferRT::parseDictionary(const Dictionary& dict) +{ + // Call the base class first. + if (!GBuffer::parseDictionary(dict)) return false; + + for (const auto& v : dict) + { + if (v.key() == kLOD) mLODMode = v.val(); + else + { + // TODO: This incorrectly logs warnings about unknown fields because some fields are parsed in the base class. Should be fixed somehow. + logWarning("Unknown field `" + v.key() + "` in a GBufferRT dictionary"); + } + } + return true; +} + +GBufferRT::SharedPtr GBufferRT::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new GBufferRT); + return pPass->parseDictionary(dict) ? pPass : nullptr; +} + +Dictionary GBufferRT::getScriptingDictionary() +{ + Dictionary dict = GBuffer::getScriptingDictionary(); + dict[kLOD] = mLODMode; + return dict; +} + +GBufferRT::GBufferRT() : GBuffer() +{ + // Create ray tracing program + RtProgram::Desc progDesc; + progDesc.addShaderLibrary(kFileRayTrace).setRayGen("rayGen"); + progDesc.addHitGroup(0, "primaryClosestHit", "primaryAnyHit").addMiss(0, "primaryMiss"); + mRaytrace.pProgram = RtProgram::create(progDesc); + if (!mRaytrace.pProgram) throw std::exception("Failed to create program"); + + // Initialize ray tracing state + mRaytrace.pState = RtState::create(); + mRaytrace.pState->setMaxTraceRecursionDepth(1); // Max trace depth 1 allows TraceRay to be called from RGS, but no secondary rays. + mRaytrace.pState->setProgram(mRaytrace.pProgram); + + // Create random engine + mpSampleGenerator = SampleGenerator::create(SAMPLE_GENERATOR_UNIFORM); + if (!mpSampleGenerator) throw std::exception("Failed to create sample generator"); + mpSampleGenerator->prepareProgram(mRaytrace.pProgram.get()); + + // Set default cull mode + setCullMode(mCullMode); +} + +void GBufferRT::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + GBuffer::setScene(pRenderContext, pScene); + + assert(pScene); + mRaytrace.pProgram->addDefines(pScene->getSceneDefines()); + mRaytrace.pVars = RtProgramVars::create(mRaytrace.pProgram, pScene); + if (!mRaytrace.pVars) throw std::exception("Failed to create program vars"); +} + +void GBufferRT::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Update refresh flag if options that affect the output have changed. + Dictionary& dict = renderData.getDictionary(); + if (mOptionsChanged) + { + auto prevFlags = (Falcor::RenderPassRefreshFlags)(dict.keyExists(kRenderPassRefreshFlags) ? dict[Falcor::kRenderPassRefreshFlags] : 0u); + dict[Falcor::kRenderPassRefreshFlags] = (uint32_t)(prevFlags | Falcor::RenderPassRefreshFlags::RenderOptionsChanged); + mOptionsChanged = false; + } + + if (mpScene == nullptr) + { + logWarning("GBufferRT::execute() - No scene available"); + return; + } + + // Setup ray flags. + if (mForceCullMode && mCullMode == RasterizerState::CullMode::Front) mGBufferParams.rayFlags = D3D12_RAY_FLAG_CULL_FRONT_FACING_TRIANGLES; + else if (mForceCullMode && mCullMode == RasterizerState::CullMode::Back) mGBufferParams.rayFlags = D3D12_RAY_FLAG_CULL_BACK_FACING_TRIANGLES; + else mGBufferParams.rayFlags = D3D12_RAY_FLAG_NONE; + + // Configure depth-of-field. + // When DOF is enabled, two PRNG dimensions are used. Pass this info to subsequent passes via the dictionary. + const bool useDOF = mpScene->getCamera()->getApertureRadius() > 0.f; + if (useDOF) dict[Falcor::kRenderPassPRNGDimension] = useDOF ? 2u : 0u; + + mRaytrace.pProgram->addDefine("USE_DEPTH_OF_FIELD", useDOF ? "1" : "0"); + mRaytrace.pProgram->addDefine("USE_RAY_DIFFERENTIALS", mLODMode == LODMode::RayDifferentials ? "1" : "0"); + + if (mLODMode == LODMode::RayDifferentials) + { + logWarning("GBufferRT::execute() - Ray differentials are not tested for instance transforms that flip the coordinate system handedness. The results may be incorrect."); + } + + GraphicsVars::SharedPtr pGlobalVars = mRaytrace.pVars->getGlobalVars(); + pGlobalVars["PerFrameCB"]["gParams"].setBlob(mGBufferParams); + + bool success = mpSampleGenerator->setIntoProgramVars(pGlobalVars.get()); + if (!success) throw std::exception("Failed to bind sample generator"); + + // Bind outputs. + auto bind = [&](const ChannelDesc& output) + { + Texture::SharedPtr pTex = renderData[output.name]->asTexture(); + if (pTex) pRenderContext->clearUAV(pTex->getUAV().get(), glm::vec4(0, 0, 0, 0)); + pGlobalVars[output.texname] = pTex; + }; + for (auto it : kGBufferChannels) bind(it); + for (auto it : kGBufferRTChannels) bind(it); + + // Launch the rays. + uvec3 targetDim = uvec3((int)mGBufferParams.frameSize.x, (int)mGBufferParams.frameSize.y, 1u); + mpScene->raytrace(pRenderContext, mRaytrace.pState, mRaytrace.pVars, targetDim); + + mGBufferParams.frameCount++; +} + +void GBufferRT::renderUI(Gui::Widgets& widget) +{ + // Render the base class UI first. + GBuffer::renderUI(widget); + + // Ray tracing specific options. + uint32_t lodMode = (uint32_t)mLODMode; + if (widget.dropdown("LOD Mode", kLODModeList, lodMode)) + { + mLODMode = (LODMode)lodMode; + mOptionsChanged = true; + } + + widget.tooltip("Enables adjustment of the shading normals to reduce the risk of black pixels due to back-facing vectors.", true); +} diff --git a/Source/RenderPasses/GBuffer/GBufferRT.h b/Source/RenderPasses/GBuffer/GBufferRT.h new file mode 100644 index 000000000..9f91af179 --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBufferRT.h @@ -0,0 +1,90 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "GBuffer.h" +#include "Utils/Sampling/SampleGenerator.h" + +using namespace Falcor; + +/** Ray traced G-buffer pass. + This pass renders a fixed set of G-buffer channels using ray tracing. +*/ +class GBufferRT : public GBuffer, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + RenderPassReflection reflect(const CompileData& compileData) override; + void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + void renderUI(Gui::Widgets& widget) override; + Dictionary getScriptingDictionary() override; + void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + std::string getDesc(void) override { return "Ray-traced GBuffer generation"; } + + enum class LODMode + { + UseMip0 = 0, // Don't compute LOD (default) + RayDifferentials = 1, // Use ray differentials + TexLODCone = 2, // Cone based LOD computation (not implemented yet) + }; + +private: + GBufferRT(); + bool parseDictionary(const Dictionary& dict) override; + + // Internal state + SampleGenerator::SharedPtr mpSampleGenerator; + + // UI variables + LODMode mLODMode = LODMode::UseMip0; + + // Ray tracing resources + struct + { + RtState::SharedPtr pState; + RtProgram::SharedPtr pProgram; + RtProgramVars::SharedPtr pVars; + } mRaytrace; +}; + +#define lod_mode_cm(a) case GBufferRT::LODMode::a: return #a +inline std::string to_string(GBufferRT::LODMode st) +{ + switch (st) + { + lod_mode_cm(UseMip0); + lod_mode_cm(RayDifferentials); + lod_mode_cm(TexLODCone); + default: + should_not_get_here(); + return ""; + } +} +#undef lod_mode_cm diff --git a/Source/RenderPasses/GBuffer/GBufferRaster.cpp b/Source/RenderPasses/GBuffer/GBufferRaster.cpp new file mode 100644 index 000000000..d4c99ed73 --- /dev/null +++ b/Source/RenderPasses/GBuffer/GBufferRaster.cpp @@ -0,0 +1,170 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Falcor.h" +#include "RenderGraph/RenderPassStandardFlags.h" +#include "GBufferRaster.h" +#include "RenderPasses/DepthPass.h" + +namespace +{ + const char kFileRasterPrimary[] = "RenderPasses/GBuffer/RasterPrimary.slang"; + + // Additional output channels. + // TODO: Some are RG32 floats now. I'm sure that all of these could be fp16. + const ChannelList kGBufferRasterChannels = + { + { "faceNormalW", "gFaceNormalW", "Face normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, + { "mvec", "gMotionVectors", "motion vectors", true /* optional */, ResourceFormat::RG32Float }, + { "pnFwidth", "gPosNormalFwidth", "position and normal filter width", true /* optional */, ResourceFormat::RG32Float }, + { "linearZ", "gLinearZAndDeriv", "linear z (and derivative)", true /* optional */, ResourceFormat::RG32Float }, + }; +} + +RenderPassReflection GBufferRaster::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + + // Add the required depth/stencil output. This always exists. + r.addOutput("depthStencil", "depth and stencil").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil); + + // Add all the other outputs. + // The default channels are written as render targets, the rest as UAVs as there is way to assign/pack render targets yet. + auto addOutput = [&](const ChannelDesc& output, Resource::BindFlags bindFlags) + { + auto& f = r.addOutput(output.name, output.desc).format(output.format).bindFlags(bindFlags); + if (output.optional) f.flags(RenderPassReflection::Field::Flags::Optional); + }; + for (auto it : kGBufferChannels) addOutput(it, Resource::BindFlags::RenderTarget); + for (auto it : kGBufferRasterChannels) addOutput(it, Resource::BindFlags::UnorderedAccess); + + return r; +} + +GBufferRaster::SharedPtr GBufferRaster::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new GBufferRaster); + return pPass->parseDictionary(dict) ? pPass : nullptr; +} + +GBufferRaster::GBufferRaster() : GBuffer() +{ + // Create raster program + mRaster.pProgram = GraphicsProgram::createFromFile(kFileRasterPrimary, "", "ps"); + if (!mRaster.pProgram) throw std::exception("Failed to create program"); + + // Initialize graphics state + mRaster.pState = GraphicsState::create(); + mRaster.pState->setProgram(mRaster.pProgram); + + // Set default cull mode + setCullMode(mCullMode); + + // Set depth function + DepthStencilState::Desc dsDesc; + dsDesc.setDepthFunc(DepthStencilState::Func::Equal).setDepthWriteMask(false); + DepthStencilState::SharedPtr pDsState = DepthStencilState::create(dsDesc); + mRaster.pState->setDepthStencilState(pDsState); + + mpFbo = Fbo::create(); +} + +void GBufferRaster::compile(RenderContext* pContext, const CompileData& compileData) +{ + GBuffer::compile(pContext, compileData); + mpDepthPrePassGraph = RenderGraph::create("Depth Pre-Pass"); + DepthPass::SharedPtr pDepthPass = DepthPass::create(pContext); + pDepthPass->setDepthBufferFormat(ResourceFormat::D32Float); + mpDepthPrePassGraph->addPass(pDepthPass, "DepthPrePass"); + mpDepthPrePassGraph->markOutput("DepthPrePass.depth"); + mpDepthPrePassGraph->setScene(mpScene); +} + +void GBufferRaster::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + GBuffer::setScene(pRenderContext, pScene); + + mRaster.pProgram->addDefines(pScene->getSceneDefines()); + mRaster.pVars = GraphicsVars::create(mRaster.pProgram.get()); + if (!mRaster.pVars) throw std::exception("Failed to create program vars"); + + if (mpDepthPrePassGraph) mpDepthPrePassGraph->setScene(pScene); +} + +void GBufferRaster::setCullMode(RasterizerState::CullMode mode) +{ + GBuffer::setCullMode(mode); + RasterizerState::Desc rsDesc; + rsDesc.setCullMode(mCullMode); + mRaster.pState->setRasterizerState(RasterizerState::create(rsDesc)); +} + +void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Update refresh flag if options that affect the output have changed. + if (mOptionsChanged) + { + Dictionary& dict = renderData.getDictionary(); + auto prevFlags = (Falcor::RenderPassRefreshFlags)(dict.keyExists(kRenderPassRefreshFlags) ? dict[Falcor::kRenderPassRefreshFlags] : 0u); + dict[Falcor::kRenderPassRefreshFlags] = (uint32_t)(prevFlags | Falcor::RenderPassRefreshFlags::RenderOptionsChanged); + mOptionsChanged = false; + } + + if (mpScene == nullptr) + { + logWarning("GBufferRaster::execute() - No scene available"); + return; + } + + mpDepthPrePassGraph->execute(pRenderContext); + mpFbo->attachDepthStencilTarget(mpDepthPrePassGraph->getOutput("DepthPrePass.depth")->asTexture()); + pRenderContext->copyResource(renderData["depthStencil"].get(), mpDepthPrePassGraph->getOutput("DepthPrePass.depth").get()); + + for (int i = 0; i < kGBufferChannels.size(); ++i) + { + Texture::SharedPtr pTex = renderData[kGBufferChannels[i].name]->asTexture(); + mpFbo->attachColorTarget(pTex, i); + } + + pRenderContext->clearFbo(mpFbo.get(), vec4(0), 1.f, 0, FboAttachmentType::Color); + mRaster.pState->setFbo(mpFbo); + + mRaster.pVars["PerFrameCB"]["gParams"].setBlob(mGBufferParams); + + // UAV output variables + for (auto it : kGBufferRasterChannels) + { + Texture::SharedPtr pTex = renderData[it.name]->asTexture(); + if (pTex) pRenderContext->clearUAV(pTex->getUAV().get(), glm::vec4(0, 0, 0, 0)); + mRaster.pVars[it.texname] = pTex; + } + + Scene::RenderFlags flags = mForceCullMode ? Scene::RenderFlags::UserRasterizerState : Scene::RenderFlags::None; + mpScene->render(pRenderContext, mRaster.pState.get(), mRaster.pVars.get(), flags); + + mGBufferParams.frameCount++; +} diff --git a/Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.h b/Source/RenderPasses/GBuffer/GBufferRaster.h similarity index 67% rename from Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.h rename to Source/RenderPasses/GBuffer/GBufferRaster.h index cec9f4040..83870cd40 100644 --- a/Samples/Raytracing/PathTracer/RenderPasses/GBufferRaster.h +++ b/Source/RenderPasses/GBuffer/GBufferRaster.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,33 +26,33 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Falcor.h" +#include "GBuffer.h" using namespace Falcor; -class GBufferRaster : public RenderPass, inherit_shared_from_this +/** Raster G-buffer pass. + This pass renders a fixed set of G-buffer channels using rasterization. +*/ +class GBufferRaster : public GBuffer, inherit_shared_from_this { public: using SharedPtr = std::shared_ptr; - static SharedPtr create(const Dictionary& dict = {}); + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); - RenderPassReflection reflect() const override; - void execute(RenderContext* pContext, const RenderData* pRenderData) override; - void renderUI(Gui* pGui, const char* uiGroup) override; - Dictionary getScriptingDictionary() const override; - void onResize(uint32_t width, uint32_t height) override; - void setScene(const std::shared_ptr& pScene) override; + RenderPassReflection reflect(const CompileData& compileData) override; + void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; std::string getDesc(void) override { return "Raster GBuffer generation"; } + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + private: GBufferRaster(); - void setCullMode(RasterizerState::CullMode mode); - bool parseDictionary(const Dictionary& dict); + void setCullMode(RasterizerState::CullMode mode) override; - GraphicsState::SharedPtr mpGraphicsState; - SceneRenderer::SharedPtr mpSceneRenderer; - Fbo::SharedPtr mpFbo; - RasterizerState::CullMode mCullMode = RasterizerState::CullMode::Back; + // Internal state + RenderGraph::SharedPtr mpDepthPrePassGraph; + Fbo::SharedPtr mpFbo; // Rasterization resources struct diff --git a/Source/RenderPasses/GBuffer/RasterPrimary.slang b/Source/RenderPasses/GBuffer/RasterPrimary.slang new file mode 100644 index 000000000..ce80e255d --- /dev/null +++ b/Source/RenderPasses/GBuffer/RasterPrimary.slang @@ -0,0 +1,102 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "VertexAttrib.h" + +import Scene; +import Shading; +import ShaderCommon; +import Raster; +import GBufferHelpers; + +struct GBufferOut +{ + float4 posW : SV_TARGET0; + float4 normW : SV_TARGET1; + float4 bitangentW : SV_TARGET2; + float4 texC : SV_TARGET3; + float4 diffuseOpacity : SV_TARGET4; + float4 specRough : SV_TARGET5; + float4 emissive : SV_TARGET6; + float4 matlExtra : SV_TARGET7; +}; + +// UAV output channels +IRWTexture2D gFaceNormalW; +IRWTexture2D gMotionVectors; +IRWTexture2D gPosNormalFwidth; +IRWTexture2D gLinearZAndDeriv; + +/** Entry point for G-buffer rasterization pixel shader. +*/ +[earlydepthstencil] +GBufferOut ps(VSOut vsOut, uint triangleIndex : SV_PrimitiveID /*, float4 pos : SV_Position*/) +{ + // BUG: When compiling with dxcompiler to shader model 6.0, the validation layers complains that SV_Position + // has overlapping semantic index at 0. This is because GBufVertexOut contains VSOut from Raster.slang, + // which in turn has a member posH with the the SV_Position semantic. So, we use that here instead. + // However, when compiling graphics shaders for SM 6.0, we get no output. It's probably not related to this, but could be. + const float4 pos = vsOut.posH; + int2 ipos = int2(pos.xy); + + float3 faceNormal = gScene.getFaceNormalW(vsOut.meshInstanceID, triangleIndex); + VertexData v = prepareVertexData(vsOut, faceNormal); + + if (alphaTest(v, gScene.materials[vsOut.materialID])) discard; + ShadingData sd = prepareShadingData(v, gScene.materials[vsOut.materialID], gScene.camera.posW); + + GBuffer gbuf = storeGBufferOutput(sd); + + // Store render target outputs. + GBufferOut gout; + gout.posW = gbuf.posW; + gout.normW = gbuf.normW; + gout.bitangentW = gbuf.bitangentW; + gout.texC = gbuf.texC; + gout.diffuseOpacity = gbuf.diffuseOpacity; + gout.specRough = gbuf.specRough; + gout.emissive = gbuf.emissive; + gout.matlExtra = gbuf.matlExtra; + + // Store UAV outputs. + gFaceNormalW[ipos] = float4(sd.faceN, 0); + + // Compute motion vectors. + const float2 pixelPos = ipos + float2(0.5, 0.5); // Current sample in pixel coords. + const float4 prevPosH = vsOut.prevPosH; // Sample in previous frame in clip space coords, no jittering applied. + const float2 mv = calcMotionVector(pixelPos, prevPosH, gParams.frameSize) + float2(gScene.camera.jitterX, -gScene.camera.jitterY); // Remove camera jitter from motion vector + gMotionVectors[ipos] = float4(mv, 0, 0); + + // Length of derivatives of position and normal + gPosNormalFwidth[ipos] = float4(length(fwidth(sd.posW)), length(fwidth(sd.N)), 0, 0); + + // Linear z and its derivative + const float linearZ = vsOut.posH.z * vsOut.posH.w; + gLinearZAndDeriv[ipos] = float4(linearZ, max(abs(ddx(linearZ)), abs(ddy(linearZ))), 0, 0); + + return gout; +} diff --git a/Source/RenderPasses/GBuffer/RaytracePrimary.rt.slang b/Source/RenderPasses/GBuffer/RaytracePrimary.rt.slang new file mode 100644 index 000000000..949c71b51 --- /dev/null +++ b/Source/RenderPasses/GBuffer/RaytracePrimary.rt.slang @@ -0,0 +1,208 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Scene; +import Raytracing; +import Utils.Math.MathHelpers; +import Utils.Sampling.SampleGenerator; +import GBufferHelpers; + +// GBuffer channels +shared IRWTexture2D gPosW; +shared IRWTexture2D gNormW; +shared IRWTexture2D gBitangentW; +shared IRWTexture2D gTexC; +shared IRWTexture2D gDiffuseOpacity; +shared IRWTexture2D gSpecRough; +shared IRWTexture2D gEmissive; +shared IRWTexture2D gMatlExtra; + +// GBufferRT channels +shared IRWTexture2D gFaceNormalW; +shared IRWTexture2D gViewW; +shared RWTexture2D gVisBuffer; + + +/** ***************************** Ray index 0 ****************************** */ + +/** Payload passed along with primary rays. + We write the G-buffer from the hit shader, so this struct is very lightweight. +*/ +struct PrimaryRayData +{ + bool hit; +}; + +[shader("miss")] +void primaryMiss(inout PrimaryRayData rayData) +{ +} + +[shader("anyhit")] +void primaryAnyHit(inout PrimaryRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + // Alpha test for non-opaque geometry. + VertexData v = getVertexData(PrimitiveIndex(), attribs); + if (alphaTest(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], 0.f)) IgnoreHit(); +} + +/** Ray differentials for primary hit. Code from RayTracingGems, Chapter 20. +*/ +void computeRayDifferentials(uint meshInstanceID, uint triangleIndex, float3 ray_dir, float ray_t, out float2 ddx, out float2 ddy, const CameraData camera) +{ + // TODO: Is this code correct for instance transforms that flip the handedness of the coordinate system? + + // Ray differentials + float3 P[3]; + gScene.getVertexPositionsW(meshInstanceID, triangleIndex, P); + float3 e1 = P[1] - P[0]; + float3 e2 = P[2] - P[0]; + float3 d = ray_dir; + float k = dot(cross(e1, e2), d); + k = abs(k) > 0.0001f ? rcp(k) : 0.0f; + float3 cu = cross(e2, d); + float3 cv = cross(d, e1); + // Assumes a normalized ray direction + float3 dx = camera.cameraU * 2.0 * gParams.invFrameSize.x / camera.focalDistance; // dDdx in ray gen + float3 dy = camera.cameraV * 2.0 * gParams.invFrameSize.y / camera.focalDistance; // dDdy in ray gen + float3 q = dx * ray_t; // Transfer to primary hit + float3 r = dy * ray_t; + float dudx = k * dot(cu, q); + float dudy = k * dot(cu, r); + float dvdx = k * dot(cv, q); + float dvdy = k * dot(cv, r); + float2 T[3]; + gScene.getVertexTexCoords(meshInstanceID, triangleIndex, T); + float2 g1 = T[1] - T[0]; + float2 g2 = T[2] - T[0]; + float dsdx = (dudx * g1.x + dvdx * g2.x); + float dsdy = (dudy * g1.x + dvdy * g2.x); + float dtdx = (dudx * g1.y + dvdx * g2.y); + float dtdy = (dudy * g1.y + dvdy * g2.y); + ddx = float2(dsdx, dtdx); + ddy = float2(dsdy, dtdy); +} + +/** Closest hit shader for primary rays. +*/ +[shader("closesthit")] +void primaryClosestHit(inout PrimaryRayData rayData, BuiltInTriangleIntersectionAttributes attribs) +{ + rayData.hit = true; + uint2 launchIndex = DispatchRaysIndex().xy; + + const uint meshInstanceID = getGlobalHitID(); + const uint triangleIndex = PrimitiveIndex(); + VertexData v = getVertexData(triangleIndex, attribs); + +#if USE_RAY_DIFFERENTIALS + float2 ddx, ddy; + computeRayDifferentials(meshInstanceID, triangleIndex, WorldRayDirection(), RayTCurrent(), ddx, ddy, gScene.camera); + ShadingData sd = prepareShadingData(v, gScene.materials[gScene.getMaterialID(meshInstanceID)], WorldRayOrigin(), ddx, ddy); +#else + ShadingData sd = prepareShadingData(v, gScene.materials[gScene.getMaterialID(meshInstanceID)], WorldRayOrigin(), 0.f); +#endif + + // Write the outputs. + GBuffer gbuf = storeGBufferOutput(sd); + + gPosW[launchIndex] = gbuf.posW; + gNormW[launchIndex] = gbuf.normW; + gBitangentW[launchIndex] = gbuf.bitangentW; + gTexC[launchIndex] = gbuf.texC; + gDiffuseOpacity[launchIndex] = gbuf.diffuseOpacity; + gSpecRough[launchIndex] = gbuf.specRough; + gEmissive[launchIndex] = gbuf.emissive; + gMatlExtra[launchIndex] = gbuf.matlExtra; + + gFaceNormalW[launchIndex] = float4(v.faceNormalW, 0.f); + gVisBuffer[launchIndex] = uint4(meshInstanceID, triangleIndex, asuint(attribs.barycentrics.x), asuint(attribs.barycentrics.y)); +} + + +/** ******************************** RayGen ******************************** */ + +// p = position on the image plane in [0,1] where (0,0) is top-left corner. +PrimaryRayData shootPrimaryRay(float2 p, inout SampleGenerator sg) +{ + // Compute primary ray's origin and direction. + float2 ndc = float2(2, -2) * p + float2(-1, 1); + float3 rayDir = ndc.x * gScene.camera.cameraU + ndc.y * gScene.camera.cameraV + gScene.camera.cameraW; // rayDir = world-space direction to point on image plane (unnormalized) + float3 origin = gScene.camera.posW; + +#if USE_DEPTH_OF_FIELD + { + // Draw sample on the lens to compute new origin and direction + float2 rnd = sampleNext2D(sg); + float2 apertureSample = sample_disk(rnd); // Sample lies in the unit disk [-1,1]^2 + + float3 rayTarget = origin + rayDir; + origin += gScene.camera.apertureRadius * (apertureSample.x * normalize(gScene.camera.cameraU) + apertureSample.y * normalize(gScene.camera.cameraV)); + rayDir = rayTarget - origin; + } +#endif + rayDir = normalize(rayDir); + + // Setup ray + RayDesc ray; + ray.Origin = origin; + ray.Direction = rayDir; + ray.TMin = 0.f; // It's OK to start at 0.0 for the camera, as it should not sit exactly on a surface + ray.TMax = 1e+38f; + // TODO: Discard hits outside [Z_near, Z_far] to match raster behavior. + + // Setup ray payload + PrimaryRayData rayData; + rayData.hit = false; + + TraceRay(gRtScene, gParams.rayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, hitProgramCount, 0 /* missIdx */, ray, rayData); + + // Write additional outputs. + uint2 launchIndex = DispatchRaysIndex().xy; + gViewW[launchIndex] = float4(-rayDir, 0.f); + + if (!rayData.hit) gVisBuffer[launchIndex] = uint4(kInvalidIndex, kInvalidIndex, 0, 0); + + return rayData; +} + +[shader("raygeneration")] +void rayGen() +{ + uint2 launchIndex = DispatchRaysIndex().xy; + uint2 launchDim = DispatchRaysDimensions().xy; + + // Compute sample position in screen space in [0,1] with origin at the top-left corner. + // The camera jitter offsets the sample by +-0.5 pixels from the pixel center. + float2 p = (launchIndex + float2(0.5f, 0.5f)) / launchDim + float2(-gScene.camera.jitterX, gScene.camera.jitterY); + + // Initialize random seed + SampleGenerator sg = SampleGenerator.create(launchIndex, gParams.frameCount); + + // Shoot primary ray. The hit shader writes the G-buffer. + shootPrimaryRay(p, sg); +} diff --git a/Source/RenderPasses/GBuffer/Testing/test_rasterGbuffer.py b/Source/RenderPasses/GBuffer/Testing/test_rasterGbuffer.py new file mode 100644 index 000000000..44abef536 --- /dev/null +++ b/Source/RenderPasses/GBuffer/Testing/test_rasterGbuffer.py @@ -0,0 +1,20 @@ +def render_graph_GBufferRaster(): + loadRenderPassLibrary("GBuffer.dll") + + tracer = RenderGraph("RasterGBuffer") + tracer.addPass(RenderPass("GBufferRaster"), "GBufferRaster") + + tracer.markOutput("GBufferRaster.posW") + tracer.markOutput("GBufferRaster.normW") + tracer.markOutput("GBufferRaster.bitangentW") + tracer.markOutput("GBufferRaster.texC") + tracer.markOutput("GBufferRaster.diffuseOpacity") + tracer.markOutput("GBufferRaster.specRough") + tracer.markOutput("GBufferRaster.emissive") + tracer.markOutput("GBufferRaster.matlExtra") + + return tracer + +GBufferRaster = render_graph_GBufferRaster() +try: m.addGraph(GBufferRaster) +except NameError: None diff --git a/Source/RenderPasses/GBuffer/Testing/test_rayGBuffer.py b/Source/RenderPasses/GBuffer/Testing/test_rayGBuffer.py new file mode 100644 index 000000000..7f793b0db --- /dev/null +++ b/Source/RenderPasses/GBuffer/Testing/test_rayGBuffer.py @@ -0,0 +1,20 @@ +def render_graph_GBufferRT(): + loadRenderPassLibrary("GBuffer.dll") + + tracer = RenderGraph("RtGbuffer") + tracer.addPass(RenderPass("GBufferRT"), "GBufferRT") + + tracer.markOutput("GBufferRT.posW") + tracer.markOutput("GBufferRT.normW") + tracer.markOutput("GBufferRT.bitangentW") + tracer.markOutput("GBufferRT.texC") + tracer.markOutput("GBufferRT.diffuseOpacity") + tracer.markOutput("GBufferRT.specRough") + tracer.markOutput("GBufferRT.emissive") + tracer.markOutput("GBufferRT.matlExtra") + + return tracer + +GBufferRT = render_graph_GBufferRT() +try: m.addGraph(GBufferRT) +except NameError: None \ No newline at end of file diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp new file mode 100644 index 000000000..5568ccc95 --- /dev/null +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp @@ -0,0 +1,306 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "MinimalPathTracer.h" +#include "RenderGraph/RenderPassHelpers.h" + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("MinimalPathTracer", "Minimal path tracer", MinimalPathTracer::create); +} + +namespace +{ + const char kShaderFile[] = "RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang"; + + // Ray tracing settings that affect the traversal stack size. + // These should be set as small as possible. + const uint32_t kMaxPayloadSizeBytes = 80u; + const uint32_t kMaxAttributesSizeBytes = 8u; + const uint32_t kMaxRecursionDepth = 2u; + + const char kViewDirInput[] = "viewW"; + + const ChannelList kInputChannels = + { + { "posW", "gWorldPosition", "World-space position (xyz) and foreground flag (w)" }, + { "normalW", "gWorldShadingNormal", "World-space shading normal (xyz)" }, + { "bitangentW", "gWorldShadingBitangent", "World-space shading bitangent (xyz)", true /* optional */ }, + { "faceNormalW", "gWorldFaceNormal", "Face normal in world space (xyz)", }, + { kViewDirInput, "gWorldView", "World-space view direction (xyz)", true /* optional */ }, + { "mtlDiffOpacity", "gMaterialDiffuseOpacity", "Material diffuse color (xyz) and opacity (w)" }, + { "mtlSpecRough", "gMaterialSpecularRoughness", "Material specular color (xyz) and roughness (w)" }, + { "mtlEmissive", "gMaterialEmissive", "Material emissive color (xyz)" }, + { "mtlParams", "gMaterialExtraParams", "Material parameters (IoR, flags etc)" }, + }; + + const ChannelList kOutputChannels = + { + { "color", "gOutputColor", "Output color (sum of direct and indirect)" }, + }; +}; + +MinimalPathTracer::SharedPtr MinimalPathTracer::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new MinimalPathTracer); + return pPass->init(dict) ? pPass : nullptr; +} + +bool MinimalPathTracer::init(const Dictionary& dict) +{ + // Deserialize pass from dictionary. + serializePass(dict); + + // Create ray tracing program. + RtProgram::Desc progDesc; + progDesc.addShaderLibrary(kShaderFile).setRayGen("rayGen"); + progDesc.addHitGroup(0, "scatterClosestHit", "scatterAnyHit").addMiss(0, "scatterMiss"); + progDesc.addHitGroup(1, "", "shadowAnyHit").addMiss(1, "shadowMiss"); + progDesc.addDefine("MAX_BOUNCES", std::to_string(mMaxBounces)); + mTracer.pProgram = RtProgram::create(progDesc, kMaxPayloadSizeBytes, kMaxAttributesSizeBytes); + if (!mTracer.pProgram) return false; + + // Setup ray tracing state. + mTracer.pState = RtState::create(); + assert(mTracer.pState); + mTracer.pState->setMaxTraceRecursionDepth(kMaxRecursionDepth); + mTracer.pState->setProgram(mTracer.pProgram); + + // Create a sample generator. + mpSampleGenerator = SampleGenerator::create(SAMPLE_GENERATOR_UNIFORM); + assert(mpSampleGenerator); + + return true; +} + +Dictionary MinimalPathTracer::getScriptingDictionary() +{ + Dictionary dict; + serializePass(dict); + return dict; +} + +RenderPassReflection MinimalPathTracer::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + + // Define our input/output channels. + for (auto it : kInputChannels) + { + auto& buf = reflector.addInput(it.name, it.desc); + buf.bindFlags(ResourceBindFlags::ShaderResource); + buf.format(it.format); + if (it.optional) buf.flags(RenderPassReflection::Field::Flags::Optional); + } + for (auto it : kOutputChannels) + { + auto& buf = reflector.addOutput(it.name, it.desc); + buf.bindFlags(ResourceBindFlags::UnorderedAccess); + buf.format(it.format); + if (it.optional) buf.flags(RenderPassReflection::Field::Flags::Optional); + } + + return reflector; +} + +void MinimalPathTracer::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Update refresh flag if options that affect the output have changed. + Dictionary& dict = renderData.getDictionary(); + if (mOptionsChanged) + { + auto prevFlags = (Falcor::RenderPassRefreshFlags)(dict.keyExists(kRenderPassRefreshFlags) ? dict[Falcor::kRenderPassRefreshFlags] : 0u); + dict[Falcor::kRenderPassRefreshFlags] = (uint32_t)(prevFlags | Falcor::RenderPassRefreshFlags::RenderOptionsChanged); + mOptionsChanged = false; + } + + // If we have no scene, just clear the outputs and return. + if (!mpScene) + { + for (auto it : kOutputChannels) + { + Texture* pDst = renderData[it.name]->asTexture().get(); + if (pDst) pRenderContext->clearTexture(pDst); + } + return; + } + + // Configure depth-of-field. + const bool useDOF = mpScene->getCamera()->getApertureRadius() > 0.f; + if (useDOF && renderData[kViewDirInput] == nullptr) + { + logWarning("Depth-of-field requires the '" + std::string(kViewDirInput) + "' input. Expect incorrect shading."); + } + + // Specialize program. + // These defines should not modify the program vars. Do not trigger program vars re-creation. + mTracer.pProgram->addDefine("MAX_BOUNCES", std::to_string(mMaxBounces)); + mTracer.pProgram->addDefine("COMPUTE_DIRECT", mComputeDirect ? "1" : "0"); + mTracer.pProgram->addDefine("USE_ANALYTIC_LIGHTS", mUseAnalyticLights ? "1" : "0"); + mTracer.pProgram->addDefine("USE_EMISSIVE_LIGHTS", mUseEmissiveLights ? "1" : "0"); + mTracer.pProgram->addDefine("USE_ENV_LIGHT", (mpEnvProbe && mUseEnvLight) ? "1" : "0"); + mTracer.pProgram->addDefine("USE_ENV_BACKGROUND", (mpEnvProbe && mUseEnvBackground) ? "1" : "0"); + + // For optional channels, set 'is_valid_' defines to inform the program of which ones it can access. + // TODO: This should be moved to a more general mechanism using Slang. + // This means we're currently always generating all outputs even though they may be unnecessary. + auto prepare = [&](const ChannelDesc& desc) + { + if (desc.optional && !desc.texname.empty()) + { + std::string define = "is_valid_" + desc.texname; + mTracer.pProgram->addDefine(define, renderData[desc.name] != nullptr ? "1" : "0"); + } + }; + for (auto channel : kInputChannels) prepare(channel); + for (auto channel : kOutputChannels) prepare(channel); + + // Prepare program vars. This may trigger shader compilation. + // The program should have all necessary defines set at this point. + if (!mTracer.pVars) prepareVars(); + assert(mTracer.pVars); + + // Set constants. + auto pVars = mTracer.pVars->getGlobalVars(); + pVars["CB"]["gFrameCount"] = mFrameCount; + pVars["CB"]["gPRNGDimension"] = dict.keyExists(kRenderPassPRNGDimension) ? dict[kRenderPassPRNGDimension] : 0u; + + // Bind I/O buffers. These needs to be done per-frame as the buffers may change anytime. + auto bind = [&](const ChannelDesc& desc) + { + if (!desc.texname.empty()) + { + auto pGlobalVars = mTracer.pVars->getGlobalVars(); + pGlobalVars[desc.texname] = renderData[desc.name]->asTexture(); + } + }; + for (auto channel : kInputChannels) bind(channel); + for (auto channel : kOutputChannels) bind(channel); + + // Get dimensions of ray dispatch. + const uvec2 targetDim = renderData.getDefaultTextureDims(); + assert(targetDim.x > 0 && targetDim.y > 0); + + // Spawn the rays. + mpScene->raytrace(pRenderContext, mTracer.pState, mTracer.pVars, uvec3(targetDim, 1)); + + mFrameCount++; +} + +void MinimalPathTracer::renderUI(Gui::Widgets& widget) +{ + bool dirty = false; + + dirty |= widget.var("Max bounces", mMaxBounces, 0u, 1u<<16); + widget.tooltip("Maximum path length for indirect illumination.\n0 = direct only\n1 = one indirect bounce etc.", true); + + dirty |= widget.checkbox("Evaluate direct illumination", mComputeDirect); + widget.tooltip("Compute direct illumination.\nIf disabled only indirect is computed (when max bounces > 0).", true); + + // Lighting controls. + auto lightsGroup = Gui::Group(widget, "Lights", true); + if (lightsGroup.open()) + { + dirty |= lightsGroup.checkbox("Use analytic lights", mUseAnalyticLights); + lightsGroup.tooltip("This enables Falcor's built-in analytic lights.\nThese are specified in the scene description (.fscene).", true); + dirty |= lightsGroup.checkbox("Use emissive lights", mUseEmissiveLights); + lightsGroup.tooltip("This enables using emissive triangles as light sources.", true); + dirty |= lightsGroup.checkbox("Use env map as light", mUseEnvLight); + lightsGroup.tooltip("This enables using the environment map as a distant light source", true); + dirty |= lightsGroup.checkbox("Use env map as background", mUseEnvBackground); + lightsGroup.text(("Env map: " + (mpEnvProbe ? mEnvProbeFilename : "N/A")).c_str()); + + lightsGroup.release(); + } + + // If rendering options that modify the output have changed, set flag to indicate that. + // In execute() we will pass the flag to other passes for reset of temporal data etc. + if (dirty) + { + mOptionsChanged = true; + } +} + +void MinimalPathTracer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + // Clear data for previous scene. + // After changing scene, the program vars should to be recreated. + mTracer.pVars = nullptr; + mpEnvProbe = nullptr; + mEnvProbeFilename = ""; + mFrameCount = 0; + + // Set new scene. + assert(pScene); + mpScene = pScene; + mTracer.pProgram->addDefines(pScene->getSceneDefines()); + + // Load environment map if scene uses one. + // We're getting the file name from the scene's LightProbe because that was used in the fscene files. + // TODO: Switch to use Scene::getEnvironmentMap() when the assets have been updated. + auto pLightProbe = mpScene->getLightProbe(); + if (pLightProbe != nullptr) + { + std::string fn = pLightProbe->getOrigTexture()->getSourceFilename(); + mpEnvProbe = EnvProbe::create(pRenderContext, fn); + mEnvProbeFilename = mpEnvProbe ? getFilenameFromPath(mpEnvProbe->getEnvMap()->getSourceFilename()) : ""; + } +} + +void MinimalPathTracer::prepareVars() +{ + assert(mpScene); + assert(mTracer.pProgram); + + // Configure program. + mpSampleGenerator->prepareProgram(mTracer.pProgram.get()); + + // Create program variables for the current program/scene. + // This may trigger shader compilation. If it fails, throw an exception to abort rendering. + mTracer.pVars = RtProgramVars::create(mTracer.pProgram, mpScene); + if (!mTracer.pVars) throw std::exception("Failed to create shader variables"); + + // Bind utility classes into shared data. + auto pGlobalVars = mTracer.pVars->getGlobalVars().get(); + bool success = mpSampleGenerator->setIntoProgramVars(pGlobalVars); + if (!success) throw std::exception("Failed to bind sample generator"); + + // Bind the light probe if one is loaded. + if (mpEnvProbe) + { + auto pCB = pGlobalVars->getConstantBuffer("CB").get(); + if (!pCB) throw std::exception("Failed to get constant buffer"); + bool success = mpEnvProbe->setIntoConstantBuffer(pGlobalVars, pCB, "gEnvProbe"); + if (!success) throw std::exception("Failed to bind environment map"); + } +} diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h new file mode 100644 index 000000000..7ad91eaf2 --- /dev/null +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h @@ -0,0 +1,116 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "Utils/Sampling/SampleGenerator.h" +#include "Experimental/Scene/Lights/EnvProbe.h" + +using namespace Falcor; + +/** Minimal path tracer. + + This pass implements a minimal brute-force path tracer. It does purposely + not use any importance sampling or other variance reduction techniques. + The output is unbiased/consistent ground truth images, against which other + renderers can be validated. +*/ +class MinimalPathTracer : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return "Minimal path tracer"; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + +private: + MinimalPathTracer() = default; + bool init(const Dictionary& dict); + void prepareVars(); + + // Internal state + Scene::SharedPtr mpScene; ///< Current scene. + SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU sample generator. + EnvProbe::SharedPtr mpEnvProbe; ///< Environment map sampling (if used). + std::string mEnvProbeFilename; ///< Name of loaded environment map (stripped of full path). + + // Configuration + uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). + bool mComputeDirect = true; ///< Compute direct illumination (otherwise indirect only). + int mUseAnalyticLights = true; ///< Use built-in analytic lights. + int mUseEmissiveLights = true; ///< Use emissive geometry as light sources. + int mUseEnvLight = true; ///< Use environment map as light source (if loaded). + int mUseEnvBackground = true; ///< Use environment map as background (if loaded). + + // Runtime data + uint mFrameCount = 0; ///< Frame count since scene was loaded. + bool mOptionsChanged = false; + + // Ray tracing program. + struct + { + RtState::SharedPtr pState; + RtProgram::SharedPtr pProgram; + RtProgramVars::SharedPtr pVars; + } mTracer; + + // Scripting +#define serialize(var) \ + if constexpr (!loadFromDict) dict[#var] = var; \ + else if (dict.keyExists(#var)) { if constexpr (std::is_same::value) var = (const std::string &)dict[#var]; else var = dict[#var]; vars.emplace(#var); } + + template + void serializePass(DictType& dict) + { + std::unordered_set vars; + + // Add variables here that should be serialized to/from the dictionary. + serialize(mMaxBounces); + serialize(mComputeDirect); + serialize(mUseAnalyticLights); + serialize(mUseEmissiveLights); + serialize(mUseEnvLight); + serialize(mUseEnvBackground); + + if constexpr (loadFromDict) + { + for (const auto& v : dict) + { + if (vars.find(v.key()) == vars.end()) logWarning("Unknown field `" + v.key() + "` in a PathTracer dictionary"); + } + } + } +#undef serialize +}; diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang new file mode 100644 index 000000000..68b60a9da --- /dev/null +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang @@ -0,0 +1,427 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Minimal path tracer. + + The purpose is to use it for validation of more complex renderers. + The implementation here should be kept as simple/naive as possible. + + At each hit point (including the primary hit loaded from the G-buffer), + analytic light sources (point, directional) are sampled uniformly using + 1 shadow ray, and 1 scatter ray is traced to sample the hemisphere. + At hit/miss the scatter ray includes light from emissive surface and + the environment map, respectively. Traversal stops at a fixed path length. + + Each type of light (analytic, emissive, env map) can be individually + enabled/disabled from the host. This clutters the code a bit, but it is + important as not all other renderes may support all three light types. + + The host sets the following defines: + + MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). + COMPUTE_DIRECT Nonzero if direct illumination should be included. + USE_ANALYTIC_LIGHTS Nonzero if Falcor's analytic lights should be used. + USE_EMISSIVE_LIGHTS Nonzero if emissive geometry should be used as lights. + USE_ENV_LIGHT Nonzero if env map is available and should be used as light source. + USE_ENV_BACKGROUND Nonzero if env map is available and should be used as background. + is_valid_ 1 if optional I/O buffer with this name should be used. +*/ + +#include "Utils/Math/MathConstants.slang" + +import Scene; +import Raytracing; +import Shading; +import Utils.Math.MathHelpers; +import Utils.Sampling.SampleGenerator; +import Experimental.Scene.Material.MaterialShading; +import Experimental.Scene.Lights.EnvProbe; +import Experimental.Scene.Lights.LightHelpers; + +shared cbuffer CB +{ + uint gFrameCount; // Frame count since scene was loaded. + uint gPRNGDimension; // First available PRNG dimension. + EnvProbe gEnvProbe; // Environment map sampling functions. +} + +// Inputs +shared Texture2D gWorldPosition; +shared Texture2D gWorldShadingNormal; +shared Texture2D gWorldShadingBitangent; // Optional +shared Texture2D gWorldFaceNormal; +shared Texture2D gWorldView; // Optional +shared Texture2D gMaterialDiffuseOpacity; +shared Texture2D gMaterialSpecularRoughness; +shared Texture2D gMaterialEmissive; +shared Texture2D gMaterialExtraParams; + +// Outputs +shared RWTexture2D gOutputColor; + +// Static configuration based on defines set from the host. +#define isValid(name) (is_valid_##name != 0) +static const uint kMaxBounces = MAX_BOUNCES; +static const bool kComputeDirect = COMPUTE_DIRECT; +static const bool kUseAnalyticLights = USE_ANALYTIC_LIGHTS; +static const bool kUseEmissiveLights = USE_EMISSIVE_LIGHTS; +static const bool kUseEnvLight = USE_ENV_LIGHT; +static const bool kUseEnvBackground = USE_ENV_BACKGROUND; +static const float3 kDefaultBackgroundColor = float3(0, 0, 0); +static const float kRayTMax = FLT_MAX; + +/** Payload for shadow ray. +*/ +struct ShadowRayData +{ + bool visible; +}; + +/** Payload for scatter ray (80B). +*/ +struct ScatterRayData +{ + float3 radiance; ///< Accumulated outgoing radiance from path. + bool terminated; ///< Set to true when path is terminated. + float3 thp; ///< Current path throughput. This is updated at each path vertex. + uint pathLength; ///< Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). Max 2^31. + float3 origin; ///< Next path segment origin. + uint _pad0; + float3 direction; ///< Next path segment direction. + uint _pad1; + + SampleGenerator sg; ///< Per-ray state for the sample generator (up to 16B). + + /** Create ray payload with default parameters. + */ + static ScatterRayData create(SampleGenerator sg) + { + ScatterRayData d; + d.terminated = false; + d.pathLength = 0; + d.radiance = float3(0, 0, 0); + d.thp = float3(1, 1, 1); + d.origin = float3(0, 0, 0); + d.direction = float3(0, 0, 0); + d.sg = sg; + return d; + } +}; + +/** Helper to load the material attributes. +*/ +MaterialParams loadMaterialParams(uint2 pixelPos) +{ + MaterialParams matParams; + matParams = matParams.init(); + + matParams.diffuseOpacity = gMaterialDiffuseOpacity[pixelPos]; + matParams.specularRoughness = gMaterialSpecularRoughness[pixelPos]; + matParams.emissive = gMaterialEmissive[pixelPos]; + matParams.extraParams = gMaterialExtraParams[pixelPos]; + + return matParams; +} + +/** Returns the primary ray's direction. +*/ +float3 getPrimaryRayDir(uint2 launchIndex, uint2 launchDim, const CameraData camera) +{ + if (isValid(gWorldView)) + { + // If we have the view vector bound as a buffer, just fetch it. No need to compute anything. + return -gWorldView[launchIndex].xyz; + } + else + { + // Compute the view vector. This must exactly match what the G-buffer pass is doing (jitter etc.). + // Note that we do not take depth-of-field into account as it would require exactly matching the + // sample generator between the passes, which is error prone. The host side will issue a warning instead. + + // Compute sample position in screen space in [0,1] with origin at the top-left corner. + // The camera jitter offsets the sample by +-0.5 pixels from the pixel center. + float2 p = (launchIndex + float2(0.5f, 0.5f)) / launchDim + float2(-camera.jitterX, camera.jitterY); + + // Compute the normalized ray direction assuming a pinhole camera. + float2 ndc = float2(2, -2) * p + float2(-1, 1); + float3 rayDir = ndc.x * camera.cameraU + ndc.y * camera.cameraV + camera.cameraW; + return normalize(rayDir); + } +} + +/** Traces a shadow ray towards a light source. + \param[in] origin Ray origin for the shadow ray. + \param[in] dir Direction from shading point towards the light source (normalized). + \param[in] distance Distance to the light source. + \return True if light is visible, false otherwise. +*/ +bool traceShadowRay(float3 origin, float3 dir, float distance) +{ + RayDesc ray; + ray.Origin = origin; + ray.Direction = dir; + ray.TMin = 0.f; + ray.TMax = distance; + + ShadowRayData rayData; + rayData.visible = false; // Set to true by miss shader if ray is not terminated before + TraceRay(gRtScene, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff /* instanceInclusionMask */, 1 /* hitIdx */, hitProgramCount, 1 /* missIdx */, ray, rayData); + + return rayData.visible; +} + +/** Traces a scatter ray based on ray parameters stored in the ray payload. + \param[in] rayData Describes the ray parameters. The struct is modified based on the result. +*/ +void traceScatterRay(inout ScatterRayData rayData) +{ + RayDesc ray; + ray.Origin = rayData.origin; + ray.Direction = rayData.direction; + ray.TMin = 0.f; + ray.TMax = kRayTMax; + + uint rayFlags = 0; // TODO: Set cull mode from the app + TraceRay(gRtScene, rayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, hitProgramCount, 0 /* missIdx */, ray, rayData); +} + +/** Evaluates the direct illumination from analytic lights. + This function samples Falcor's light list uniformly with one shadow ray. + \param[in] sd Shading data. + \param[in] rayOrigin Ray origin for the shadow ray. + \param[in] sg SampleGenerator object. + \return Outgoing radiance in view direction. +*/ +float3 evalDirectAnalytic(const ShadingData sd, float3 rayOrigin, inout SampleGenerator sg) +{ + // Pick one of the analytic light sources randomly with equal probability. + const uint lightCount = gScene.getLightCount(); + const uint lightIndex = min(uint(sampleNext1D(sg) * lightCount), lightCount - 1); + float invPdf = lightCount; // Light selection pdf = 1.0 / lightCount. + + // Sample local light source. + AnalyticLightSample ls; + sampleLight(rayOrigin, gScene.getLight(lightIndex), sampleNext2D(sg), ls); + + // Reject sample if lower hemisphere. + if (dot(ls.dir, sd.N) <= kMinCosTheta) return float3(0, 0, 0); + + // Test visibility by tracing a shadow ray. + bool V = traceShadowRay(rayOrigin, ls.dir, ls.distance); + return V ? evalBRDFCosine(sd, ls.dir) * ls.Li * invPdf : float3(0); +} + +/** Processes a hit point to generate a scatter ray or terminate. + This function generates a cosine-weighted direction over the hemisphere. + \param[in] sd Shading data. + \param[in] rayOrigin Ray origin for the new ray. + \param[in] rayData Ray payload. +*/ +void generateScatterRay(const ShadingData sd, float3 rayOrigin, inout ScatterRayData rayData) +{ + // Generate scatter ray as cosine-weighted direction. + float pdf; + float3 dir = sampleHemisphereCosine(sd, sampleNext2D(rayData.sg), pdf); // pdf = cos(theta) / pi + rayData.origin = rayOrigin; + rayData.direction = dir; + rayData.thp *= evalBRDF(sd, dir) * M_PI; // dot(N,L) / pdf = pi +} + +/** ********************* Ray index 0: Scatter ray ************************ */ + +[shader("miss")] +void scatterMiss(inout ScatterRayData rayData : SV_RayPayload) +{ + // Ray missed the scene. Mark the ray as terminated. + rayData.terminated = true; + + // Add contribution from distant light (env map) in this direction. + if (kUseEnvLight && (kComputeDirect || rayData.pathLength > 0)) + { + float3 Le = evalEnvProbe(gEnvProbe, WorldRayDirection()); + rayData.radiance += rayData.thp * Le; + } +} + +[shader("anyhit")] +void scatterAnyHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + // Alpha test for non-opaque geometry. + VertexData v = getVertexData(PrimitiveIndex(), attribs); + if (alphaTest(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], 0.f)) IgnoreHit(); +} + +[shader("closesthit")] +void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + // Evaluate Falcor's material parameters at the hit point. + // Note we pass hitPos-rayDir as "camera position" to avoid zero-length rays causing NaNs + // in the view direction. It'd been cleaner if prepareShadingData() took ray dir directly. + // TODO: Implement texLOD to enable texture filtering in prepareShadingData(). + const float3 rayDir = WorldRayDirection(); + VertexData v = getVertexData(PrimitiveIndex(), attribs); + ShadingData sd = prepareShadingData(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], v.posW - rayDir, 0.f); + + // Compute tangent space if it is invalid. + if (!(dot(sd.B, sd.B) > 0.f)) // Note: Comparison written so that NaNs trigger + { + sd.B = perp_stark(sd.N); + sd.T = cross(sd.B, sd.N); + } + + // Add emitted light. + if (kUseEmissiveLights && (kComputeDirect || rayData.pathLength > 0)) + { + rayData.radiance += rayData.thp * sd.emissive; + } + + // Check whether to terminate based on max depth. + if (rayData.pathLength >= kMaxBounces) + { + rayData.terminated = true; + return; + } + + // Compute ray origin for new rays spawned from the hit. + float3 rayOrigin = sd.computeNewRayOrigin(); + + // Add contribution of direct light from analytic lights. + if (kUseAnalyticLights) + { + float3 Lr = evalDirectAnalytic(sd, rayOrigin, rayData.sg); + rayData.radiance += rayData.thp * Lr; + } + + // Generate scatter ray for the next path segment. + // The raygen shader will continue the path based on the returned payload. + generateScatterRay(sd, rayOrigin, rayData); + + rayData.pathLength++; +} + +/************************** Ray index 1: Shadow ray ************************ */ + +[shader("miss")] +void shadowMiss(inout ShadowRayData rayData : SV_RayPayload) +{ + // The miss shader is executed if the ray misses all geometry. Mark as visible. + rayData.visible = true; +} + +[shader("anyhit")] +void shadowAnyHit(inout ShadowRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + // Alpha test for non-opaque geometry. + VertexData v = getVertexData(PrimitiveIndex(), attribs); + if (alphaTest(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], 0.f)) IgnoreHit(); +} + +/** ******************************** RayGen ******************************** */ + +/** This is the entry point for the minimal path tracer. + + One path per pixel is generated, which is traced into the scene. + The path tracer is written as a for-loop over path segments. + + Built-in light sources (point, directional) are sampled explicitly at each + path vertex. The contributions from area lights (env map and mesh lights) + are explicitly added by the scatter ray hit/miss shaders. +*/ +[shader("raygeneration")] +void rayGen() +{ + uint2 launchIndex = DispatchRaysIndex().xy; + uint2 launchDim = DispatchRaysDimensions().xy; + + float3 outColor = float3(0, 0, 0); + + const float3 rayDir = getPrimaryRayDir(launchIndex, launchDim, gScene.camera); + const float4 worldPos = gWorldPosition[launchIndex]; + + if (worldPos.w != 0.f) // Using w to indicate valid geometry for now + { + // Pixel represents a valid primary hit. Compute its contribution. + + // Load geometry parameters from G-buffer. + // TODO: Load (u,v) channel if it exists. + float3 normal = gWorldShadingNormal[launchIndex].xyz; + float3 bitangent = isValid(gWorldShadingBitangent) ? gWorldShadingBitangent[launchIndex].xyz : perp_stark(normal); + float3 faceNormal = gWorldFaceNormal[launchIndex].xyz; + GeometryParams geoParams = prepareGeometryParams(worldPos.xyz, -rayDir, normal, bitangent, faceNormal); + + // Load material parameters from G-buffer. + MaterialParams matParams = loadMaterialParams(launchIndex); + + // Prepare ShadingData struct. + ShadingData sd = prepareShadingData(geoParams, matParams); + + // Create sample generator. + SampleGenerator sg = SampleGenerator.create(launchIndex, gFrameCount); + + // Advance the generator to the first available dimension. + // TODO: This is potentially expensive. We may want to store/restore the state from memory if it becomes a problem. + for (uint i = 0; i < gPRNGDimension; i++) sampleNext1D(sg); + + // Compute ray origin for new rays spawned from the G-buffer. + const float3 rayOrigin = sd.computeNewRayOrigin(); + + if (kComputeDirect) + { + // Always output directly emitted light, independent of whether emissive materials are treated as light sources or not. + outColor += sd.emissive; + + // Add contribution of direct light from analytic lights. + // Light probe and mesh lights are handled by the scatter ray hit/miss shaders. + outColor += kUseAnalyticLights ? evalDirectAnalytic(sd, rayOrigin, sg) : float3(0, 0, 0); + } + + // Prepare ray payload. + ScatterRayData rayData = ScatterRayData.create(sg); + + // Generate scatter ray. + generateScatterRay(sd, rayOrigin, rayData); + + // Follow path into the scene and compute its total contribution. + for (uint depth = 0; depth <= kMaxBounces && !rayData.terminated; depth++) + { + // Trace scatter ray. If it hits geometry, the closest hit shader samples + // direct illumination and generates the next scatter ray. + traceScatterRay(rayData); + } + + // Store contribution from scatter ray. + outColor += rayData.radiance; + } + else + { + // Background pixel. + outColor = kUseEnvBackground ? evalEnvProbe(gEnvProbe, rayDir) : kDefaultBackgroundColor; + } + + gOutputColor[launchIndex] = float4(outColor, 1); +} diff --git a/Samples/Core/ComputeShader/ComputeShader.vcxproj b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj similarity index 69% rename from Samples/Core/ComputeShader/ComputeShader.vcxproj rename to Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj index ce202e1f1..f28643108 100644 --- a/Samples/Core/ComputeShader/ComputeShader.vcxproj +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj @@ -1,4 +1,4 @@ - + @@ -10,52 +10,52 @@ x64 + + {FF7FE9B8-2ACE-4044-869D-A5C4EF8042D3} + Win32Proj + MinimalPathTracer + 10.0.17763.0 + MinimalPathTracer + + + + + + + + - + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + - + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - + - - true - true - + - - {283B18E4-08BC-4CDE-BDB6-B3B70FB7FC18} - Win32Proj - ComputeShader - 10.0.17763.0 - - - Application + DynamicLibrary true v141 Unicode + Data\RenderPasses\$(ProjectName) - Application + DynamicLibrary false v141 true Unicode + Data\RenderPasses\$(ProjectName) - - - - - - true @@ -69,7 +69,10 @@ Level3 Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows @@ -84,7 +87,10 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows diff --git a/Samples/Effects/Shadows/Shadows.filters b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj.filters similarity index 51% rename from Samples/Effects/Shadows/Shadows.filters rename to Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj.filters index 3181d4117..eb84adbd4 100644 --- a/Samples/Effects/Shadows/Shadows.filters +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.vcxproj.filters @@ -1,11 +1,12 @@  - - + - - + + + + \ No newline at end of file diff --git a/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.cpp b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.cpp new file mode 100644 index 000000000..96c926c36 --- /dev/null +++ b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + # Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ +#include "PassLibraryTemplate.h" + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("RenderPassTemplate", "Render Pass Template", RenderPassTemplate::create); +} + +RenderPassTemplate::SharedPtr RenderPassTemplate::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new RenderPassTemplate); + return pPass; +} + +Dictionary RenderPassTemplate::getScriptingDictionary() +{ + return Dictionary(); +} + +RenderPassReflection RenderPassTemplate::reflect(const CompileData& compileData) +{ + // Define the required resources here + RenderPassReflection reflector; + //reflector.addOutput("dst"); + //reflector.addInput("src"); + return reflector; +} + +void RenderPassTemplate::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // renderData holds the requested resources + // auto& pTexture = renderData["src"]->asTexture(); +} + +void RenderPassTemplate::renderUI(Gui::Widgets& widget) +{ +} diff --git a/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.h b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.h new file mode 100644 index 000000000..399863028 --- /dev/null +++ b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.h @@ -0,0 +1,37 @@ +/*************************************************************************** + # Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ +#pragma once +#include "Falcor.h" +#include "FalcorExperimental.h" + +using namespace Falcor; + +class RenderPassTemplate : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + /** Create a new object + */ + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return "Insert pass description here"; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override {} + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override {} + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + +private: + RenderPassTemplate() = default; +}; diff --git a/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj new file mode 100644 index 000000000..94b49ff42 --- /dev/null +++ b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {6CC70B9F-CA56-4EF2-8075-A1BE0005DCF4} + Win32Proj + PassLibraryTemplate + 10.0.17763.0 + PassLibraryTemplate + + + + + + + + + + + + + + + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + + {f98b552e-fdd6-42a0-b30f-675644426045} + + + + DynamicLibrary + true + v141 + Unicode + Data\RenderPasses\$(ProjectName) + + + DynamicLibrary + false + v141 + true + Unicode + Data\RenderPasses\$(ProjectName) + + + + + + + true + + + false + + + + + + Level3 + Disabled + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj.filters b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj.filters similarity index 66% rename from Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj.filters rename to Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj.filters index 0ab6e29d1..c0082e3c9 100644 --- a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj.filters +++ b/Source/RenderPasses/PassLibraryTemplate/PassLibraryTemplate.vcxproj.filters @@ -1,9 +1,9 @@  - + - + \ No newline at end of file diff --git a/Source/RenderPasses/PathTracer/LoadGBuffer.slang b/Source/RenderPasses/PathTracer/LoadGBuffer.slang new file mode 100644 index 000000000..66f6984e3 --- /dev/null +++ b/Source/RenderPasses/PathTracer/LoadGBuffer.slang @@ -0,0 +1,125 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** G-buffer resources and helpers functions for loading the data. + + loadShadingData() loads everything and prepares the ShadingData struct. + + It is assumed the host sets up a define for all optional input buffers: + is_valid_ is 1 if buffer with this name is bound, 0 otherwise. +*/ + +__exported import Shading; +import Utils.Math.MathHelpers; +import Experimental.Scene.Material.MaterialHelpers; + +// G-buffer inputs +shared Texture2D gWorldPosition; +shared Texture2D gWorldShadingNormal; +shared Texture2D gWorldShadingBitangent; // Optional +shared Texture2D gWorldView; // Optional +shared Texture2D gWorldFaceNormal; +shared Texture2D gMaterialDiffuseOpacity; +shared Texture2D gMaterialSpecularRoughness; +shared Texture2D gMaterialEmissive; +shared Texture2D gMaterialExtraParams; + +#define isValid(name) (is_valid_##name != 0) + + +/** Returns the primary ray's direction. +*/ +float3 getPrimaryRayDir(uint2 pixel, uint2 frameDim, const CameraData camera) +{ + if (isValid(gWorldView)) + { + // If we have the view vector bound as a buffer, just fetch it. No need to compute anything. + return -gWorldView[pixel].xyz; + } + else + { + // Compute the view vector. This must exactly match what the G-buffer pass is doing (jitter etc.). + // Note that we do not take depth-of-field into account as it would require exactly matching the + // sample generator between the passes, which is error prone. The host side will issue a warning instead. + + // Compute sample position in screen space in [0,1] with origin at the top-left corner. + // The camera jitter offsets the sample by +-0.5 pixels from the pixel center. + float2 p = (pixel + float2(0.5f, 0.5f)) / frameDim + float2(-camera.jitterX, camera.jitterY); + + // Compute the normalized ray direction assuming a pinhole camera. + float2 ndc = float2(2, -2) * p + float2(-1, 1); + float3 rayDir = ndc.x * camera.cameraU + ndc.y * camera.cameraV + camera.cameraW; + return normalize(rayDir); + } +} + +/** Helper to load the material attributes. +*/ +MaterialParams loadMaterialParams(uint2 pixelPos) +{ + MaterialParams matParams; + matParams = matParams.init(); + + matParams.diffuseOpacity = gMaterialDiffuseOpacity[pixelPos]; + matParams.specularRoughness = gMaterialSpecularRoughness[pixelPos]; + matParams.emissive = gMaterialEmissive[pixelPos]; + matParams.extraParams = gMaterialExtraParams[pixelPos]; + + return matParams; +} + +/** Helper for setting up the ShadingData struct based on loaded data. + \param[in] pixel Current pixel coordinates. + \param[in] frameDim Frame dimensions in pixel. + \param[out] sd ShadingData struct. + \return True if the pixel has valid data (not a background pixel). Note sd.V is always valid. +*/ +bool loadShadingData(uint2 pixel, uint2 frameDim, const CameraData camera, out ShadingData sd) +{ + sd = {}; + + float3 rayDir = getPrimaryRayDir(pixel, frameDim, camera); + float4 worldPos = gWorldPosition[pixel]; + bool valid = false; + + if (worldPos.w != 0.f) // Using w to indicate valid geometry for now. + { + // Load geometry and material parameters from G-buffer. + float3 normal = gWorldShadingNormal[pixel].xyz; + float3 bitangent = isValid(gWorldShadingBitangent) ? gWorldShadingBitangent[pixel].xyz : perp_stark(normal); + float3 faceNormal = gWorldFaceNormal[pixel].xyz; + GeometryParams geoParams = prepareGeometryParams(worldPos.xyz, -rayDir, normal, bitangent, faceNormal); + MaterialParams matParams = loadMaterialParams(pixel); + + sd = prepareShadingData(geoParams, matParams); + valid = true; + } + + sd.V = -rayDir; + return valid; +} diff --git a/Source/RenderPasses/PathTracer/Logging.cpp b/Source/RenderPasses/PathTracer/Logging.cpp new file mode 100644 index 000000000..2b75bfcc1 --- /dev/null +++ b/Source/RenderPasses/PathTracer/Logging.cpp @@ -0,0 +1,168 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Logging.h" +#include +#include + +Logging::SharedPtr Logging::create() +{ + return SharedPtr(new Logging()); +} + +void Logging::begin(RenderContext* pRenderContext, const RtProgram::SharedPtr& pProgram, const RtProgramVars::SharedPtr& pVars, const glm::uvec2& frameDim) +{ + // Prepare state. + assert(!mRunning); + mRunning = true; + mWaitingForData = false; + mFrameDim = frameDim; + + pProgram->addDefine("_LOGGING_ENABLE_STATS", mStatsEnabled ? "1" : "0"); + + // Mark previously stored data as invalid. The config may have changed, so this is the safe bet. + mStats = Stats(); + mStatsValid = false; + mStatsBuffersValid = false; + + if (mStatsEnabled) + { + // Create parallel reduction helper. + if (!mpParallelReduction) + { + mpParallelReduction = ComputeParallelReduction::create(); + if (!mpParallelReduction) throw std::exception("Failed to create ComputeParallelReduction object"); + + mpReductionResult = Buffer::create(32, ResourceBindFlags::None, Buffer::CpuAccess::Read); + } + + // Prepare stats buffer. + if (!mpStatsRayCount || mpStatsRayCount->getWidth() != frameDim.x || mpStatsRayCount->getHeight() != frameDim.y) + { + mpStatsRayCount = Texture::create2D(frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsPathLength = Texture::create2D(frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + } + + assert(mpStatsRayCount && mpStatsPathLength); + pRenderContext->clearUAV(mpStatsRayCount->getUAV().get(), uvec4(0, 0, 0, 0)); + pRenderContext->clearUAV(mpStatsPathLength->getUAV().get(), uvec4(0, 0, 0, 0)); + + auto pGlobalVars = pVars->getGlobalVars(); + pGlobalVars["gStatsRayCount"] = mpStatsRayCount; + pGlobalVars["gStatsPathLength"] = mpStatsPathLength; + } +} + +void Logging::end(RenderContext* pRenderContext) +{ + assert(mRunning); + mRunning = false; + + if (mStatsEnabled) + { + // Create fence first time we need it. + if (!mpFence) mpFence = GpuFence::create(); + + // Sum of the per-pixel counters. The results are copied to a GPU buffer. + mpParallelReduction->execute(pRenderContext, mpStatsRayCount, ComputeParallelReduction::Type::Sum, nullptr, mpReductionResult, 0); + mpParallelReduction->execute(pRenderContext, mpStatsPathLength, ComputeParallelReduction::Type::Sum, nullptr, mpReductionResult, 16); + + // Submit command list and insert signal. + pRenderContext->flush(false); + mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + + mStatsBuffersValid = true; + mWaitingForData = true; + } +} + +void Logging::renderUI(Gui::Widgets& widget) +{ + // Configuration. + widget.checkbox("Traversal stats", mStatsEnabled); + widget.tooltip("Collects ray tracing traversal stats on the GPU.\nNote that this option slows down the performance."); + + // Fetch data and show stats if available. + copyStatsToCPU(); + if (mStatsValid) + { + std::ostringstream oss; + oss << "Path length (avg): " << std::fixed << std::setprecision(3) << mStats.avgPathLength << "\n"; + oss << "Traced rays (avg): " << std::fixed << std::setprecision(3) << mStats.avgRaysPerPixel << "\n"; + oss << "Traced rays (total): " << mStats.totalRays << "\n"; + widget.text(oss.str().c_str()); + } +} + +bool Logging::getStats(Logging::Stats& stats) +{ + copyStatsToCPU(); + if (!mStatsValid) + { + logWarning("Logging::getStats() - Stats are not valid. Ignoring."); + return false; + } + stats = mStats; + return true; +} + +const Texture::SharedPtr Logging::getRayCountBuffer() const +{ + assert(!mRunning); + return mStatsBuffersValid ? mpStatsRayCount : nullptr; +} + +void Logging::copyStatsToCPU() +{ + assert(!mRunning); + if (mWaitingForData) + { + // Wait for signal. + mpFence->syncCpu(); + mWaitingForData = false; + + if (mStatsEnabled) + { + // Map the stats buffer. + const uint4* data = static_cast(mpReductionResult->map(Buffer::MapType::Read)); + assert(data); + const uint32_t totalRayCount = data[0].x; + const uint32_t totalPathLength = data[1].x; + mpReductionResult->unmap(); + + // Store stats locally. + const uint32_t numPixels = mFrameDim.x * mFrameDim.y; + assert(numPixels > 0); + + mStats.totalRays = totalRayCount; + mStats.avgRaysPerPixel = (float)totalRayCount / numPixels; + mStats.avgPathLength = (float)totalPathLength / numPixels; + + mStatsValid = true; + } + } +} diff --git a/Source/RenderPasses/PathTracer/Logging.h b/Source/RenderPasses/PathTracer/Logging.h new file mode 100644 index 000000000..39aa248d8 --- /dev/null +++ b/Source/RenderPasses/PathTracer/Logging.h @@ -0,0 +1,93 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "Utils/Algorithm/ComputeParallelReduction.h" + +using namespace Falcor; + +/** Helper class for collecting runtime stats in the path tracer. + + We log per-pixel stats in buffers on the GPU, which are immediately ready for consumption + after end() is called. These stats are summarized in a reduction pass, which are + available in getStats() or printStats() after async readback to the CPU. +*/ +class Logging +{ +public: + struct Stats + { + uint32_t totalRays = 0; + float avgRaysPerPixel = 0.f; + float avgPathLength = 0.f; + }; + + using SharedPtr = std::shared_ptr; + virtual ~Logging() = default; + + static SharedPtr create(); + + void begin(RenderContext* pRenderContext, const RtProgram::SharedPtr& pProgram, const RtProgramVars::SharedPtr& pVars, const glm::uvec2& frameDim); + void end(RenderContext* pRenderContext); + void renderUI(Gui::Widgets& widget); + + /** Fetches the latest stats generated by begin()/end(). + \param[out] stats The stats are copied here. + \return True if stats are available, false otherwise. + */ + bool getStats(Logging::Stats& stats); + + /** Returns the per-pixel ray count buffer or nullptr if not available. + \return Texture in R32Uint format containing per-pixel ray counts, or nullptr if not available. + */ + const Texture::SharedPtr getRayCountBuffer() const; + +protected: + Logging() {} + void copyStatsToCPU(); + + // Internal state + ComputeParallelReduction::SharedPtr mpParallelReduction; ///< Helper for parallel reduction on the GPU. + Buffer::SharedPtr mpReductionResult; ///< Results buffer for stats readback (CPU mappable). + GpuFence::SharedPtr mpFence; ///< GPU fence for sychronizing readback. + + // Configuration + bool mStatsEnabled = false; ///< UI variable to turn logging on/off. + + // Runtime data + bool mRunning = false; ///< True inbetween begin() / end() calls. + bool mWaitingForData = false; ///< True if we are waiting for data to become available on the GPU. + glm::uvec2 mFrameDim = { 0, 0 }; ///< Frame dimensions at last call to begin(). + + bool mStatsValid = false; ///< True if stats have been read back and are valid. + Stats mStats; ///< Traversal stats. + + Texture::SharedPtr mpStatsRayCount; ///< Stats for number of rays traced. + Texture::SharedPtr mpStatsPathLength; ///< Stats for path length. + bool mStatsBuffersValid = false; ///< True if per-pixel stats buffers contain valid data. +}; diff --git a/Source/RenderPasses/PathTracer/Logging.slang b/Source/RenderPasses/PathTracer/Logging.slang new file mode 100644 index 000000000..b21b6dcaa --- /dev/null +++ b/Source/RenderPasses/PathTracer/Logging.slang @@ -0,0 +1,57 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Functionality for collecting runtime stats in the path tracer. + + Note that the log*() functions must be called from a ray tracing + program as they use DispatchRaysIndex() to identify the current pixel. + + The host sets the following defines: + + _LOGGING_ENABLE_STATS Nonzero if stats should be collected. + +*/ + +shared RWTexture2D gStatsRayCount; // Per-pixel ray count stats. +shared RWTexture2D gStatsPathLength; // Per-pixel path length. + +void logTraceRay() +{ +#if _LOGGING_ENABLE_STATS + uint2 launchIndex = DispatchRaysIndex().xy; + InterlockedAdd(gStatsRayCount[launchIndex], 1); +#endif +} + +void logPathLength(uint pathLength) +{ +#if _LOGGING_ENABLE_STATS + uint2 launchIndex = DispatchRaysIndex().xy; + gStatsPathLength[launchIndex] = pathLength; +#endif +} diff --git a/Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.cpp b/Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.cpp new file mode 100644 index 000000000..f66ffd7c7 --- /dev/null +++ b/Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "MegakernelPathTracer.h" +#include "RenderGraph/RenderPassHelpers.h" +#include + +namespace +{ + const char kShaderFile[] = "RenderPasses/PathTracer/Megakernel/PathTracer.rt.slang"; + const char kParameterBlockName[] = "gData"; + + // Ray tracing settings that affect the traversal stack size. + // These should be set as small as possible. + const uint32_t kMaxPayloadSizeBytes = 96; + const uint32_t kMaxAttributesSizeBytes = 8; + const uint32_t kMaxRecursionDepth = 1; +}; + +const char* MegakernelPathTracer::sDesc = "Megakernel path tracer"; + +MegakernelPathTracer::SharedPtr MegakernelPathTracer::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new MegakernelPathTracer); + return pPass->init(dict) ? pPass : nullptr; +} + +bool MegakernelPathTracer::init(const Dictionary& dict) +{ + // Call the base class first. + if (!PathTracer::init(dict)) return false; + + // Create ray tracing program. + RtProgram::Desc progDesc; + progDesc.addShaderLibrary(kShaderFile).setRayGen("rayGen"); + progDesc.addHitGroup(kRayTypeScatter, "scatterClosestHit", "scatterAnyHit").addMiss(kRayTypeScatter, "scatterMiss"); + progDesc.addHitGroup(kRayTypeShadow, "", "shadowAnyHit").addMiss(kRayTypeShadow, "shadowMiss"); + progDesc.addDefine("MAX_BOUNCES", std::to_string(mSharedParams.maxBounces)); + progDesc.addDefine("SAMPLES_PER_PIXEL", std::to_string(mSharedParams.samplesPerPixel)); + mTracer.pProgram = RtProgram::create(progDesc, kMaxPayloadSizeBytes, kMaxAttributesSizeBytes); + if (!mTracer.pProgram) return false; + + // Setup ray tracing state. + mTracer.pState = RtState::create(); + assert(mTracer.pState); + mTracer.pState->setMaxTraceRecursionDepth(kMaxRecursionDepth); + mTracer.pState->setProgram(mTracer.pProgram); + + return true; +} + +void MegakernelPathTracer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + PathTracer::setScene(pRenderContext, pScene); + + assert(pScene); + mTracer.pProgram->addDefines(pScene->getSceneDefines()); +} + +void MegakernelPathTracer::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + // TODO: Remove this check when the code has been generalized. + if (mSharedParams.lightSamplesPerVertex != 1) + { + logError("MegakernelPathTracer currently requires 1 light sample per path vertex. Resetting to one."); + mSharedParams.lightSamplesPerVertex = 1; + } + + // Call shared pre-render code. + if (!beginFrame(pRenderContext, renderData)) return; + + // Set compile-time constants. + RtProgram::SharedPtr pProgram = mTracer.pProgram; + setStaticParams(pProgram.get()); + + // For optional channels, set 'is_valid_' defines to inform the program of which ones it can access. + // TODO: This should be moved to a more general mechanism using Slang. + Program::DefineList defines; + auto prepare = [&](const ChannelDesc& desc) + { + if (desc.optional && !desc.texname.empty()) + { + std::string define = "is_valid_" + std::string(desc.texname); + defines.add(define, renderData[desc.name] != nullptr ? "1" : "0"); + } + }; + for (auto channel : kInputChannels) prepare(channel); + for (auto channel : kOutputChannels) prepare(channel); + pProgram->addDefines(defines); + + if (mUseEmissiveSampler) + { + // Specialize program for the current emissive light sampler options. + assert(mpEmissiveSampler); + if (mpEmissiveSampler->prepareProgram(pProgram.get())) mTracer.pVars = nullptr; + } + + // Prepare program vars. This may trigger shader compilation. + // The program should have all necessary defines set at this point. + if (!mTracer.pVars) prepareVars(); + assert(mTracer.pVars); + + // Set shared data into parameter block. + setTracerData(renderData); + + // Bind I/O buffers. These needs to be done per-frame as the buffers may change anytime. + auto bind = [&](const ChannelDesc& desc) + { + if (!desc.texname.empty()) + { + auto pGlobalVars = mTracer.pVars->getGlobalVars(); + pGlobalVars[desc.texname] = renderData[desc.name]->asTexture(); + } + }; + for (auto channel : kInputChannels) bind(channel); + for (auto channel : kOutputChannels) bind(channel); + + // Get dimensions of ray dispatch. + const uvec2 targetDim = renderData.getDefaultTextureDims(); + assert(targetDim.x > 0 && targetDim.y > 0); + + mPixelDebugger->begin(pRenderContext, pProgram, mTracer.pVars, targetDim); + mStatsLogger->begin(pRenderContext, pProgram, mTracer.pVars, targetDim); + + // Spawn the rays. + { + PROFILE("MegakernelPathTracer::execute()_RayTrace"); + mpScene->raytrace(pRenderContext, mTracer.pState, mTracer.pVars, uvec3(targetDim, 1)); + } + + mStatsLogger->end(pRenderContext); + mPixelDebugger->end(pRenderContext); + + // Call shared post-render code. + endFrame(pRenderContext, renderData); +} + +void MegakernelPathTracer::prepareVars() +{ + assert(mpScene); + assert(mTracer.pProgram); + + // Configure program. + mpSampleGenerator->prepareProgram(mTracer.pProgram.get()); + + // Create program variables for the current program/scene. + // This may trigger shader compilation. If it fails, throw an exception to abort rendering. + mTracer.pVars = RtProgramVars::create(mTracer.pProgram, mpScene); + if (!mTracer.pVars) throw std::exception("Failed to create shader variables"); + + // Bind utility classes into shared data. + auto pGlobalVars = mTracer.pVars->getGlobalVars(); + bool success = mpSampleGenerator->setIntoProgramVars(pGlobalVars.get()); + if (!success) throw std::exception("Failed to bind sample generator"); + + // Create parameter block for shared data. + ProgramReflection::SharedConstPtr pReflection = mTracer.pProgram->getGlobalReflector(); + ParameterBlockReflection::SharedConstPtr pBlockReflection = pReflection->getParameterBlock(kParameterBlockName); + assert(pBlockReflection); + mTracer.pParameterBlock = ParameterBlock::create(pBlockReflection, true); + assert(mTracer.pParameterBlock); + + // Bind static resources to the parameter block here. No need to rebind them every frame if they don't change. + // Bind the light probe if one is loaded. + if (mpEnvProbe) + { + bool success = mpEnvProbe->setIntoParameterBlock(mTracer.pParameterBlock.get(), "envProbe"); + if (!success) throw std::exception("Failed to bind environment map"); + } + + // Bind the parameter block to the global program variables. + mTracer.pVars->getGlobalVars()->setParameterBlock(kParameterBlockName, mTracer.pParameterBlock); +} + +void MegakernelPathTracer::setTracerData(const RenderData& renderData) +{ + auto pBlock = mTracer.pParameterBlock; + assert(pBlock); + + // Upload parameters struct. + pBlock->getDefaultConstantBuffer()["params"].setBlob(mSharedParams); + + // Bind emissive light sampler. + if (mUseEmissiveSampler) + { + assert(mpEmissiveSampler); + bool success = mpEmissiveSampler->setIntoParameterBlock(pBlock, "emissiveSampler"); + if (!success) throw std::exception("Failed to bind emissive light sampler"); + } +} diff --git a/Samples/Effects/HashedAlpha/HashedAlpha.h b/Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.h similarity index 53% rename from Samples/Effects/HashedAlpha/HashedAlpha.h rename to Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.h index 0219f7099..cc2206857 100644 --- a/Samples/Effects/HashedAlpha/HashedAlpha.h +++ b/Source/RenderPasses/PathTracer/Megakernel/MegakernelPathTracer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,43 +26,44 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include "Falcor.h" +#include "../PathTracer.h" -using namespace Falcor; +/** Forward path tracer using a megakernel in DXR. -class HashedAlpha : public Renderer + The path tracer has a loop over the wavefronts in the raygen shader. + The kernel terminates when all paths have terminated. + + This pass implements a forward path tracer with next-event estimation, + Russian roulette, and multiple importance sampling (MIS) with sampling + of BRDFs and light sources. +*/ +class MegakernelPathTracer : public PathTracer, inherit_shared_from_this { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; + using SharedPtr = std::shared_ptr; -private: - void loadModel(); - void loadModel(std::string filename); - void updateProgram(); + static SharedPtr create(RenderContext* pRenderContext, const Dictionary& dict); - enum class AlphaTestMode - { - HashedAlphaIsotropic, - HashedAlphaAnisotropic, - AlphaTest - }; + virtual std::string getDesc() override { return sDesc; } + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; - GraphicsProgram::SharedPtr mpProgram; - GraphicsVars::SharedPtr mpVars; - GraphicsState::SharedPtr mpState; + static const char* sDesc; - Model::SharedPtr mpModel; - Camera::SharedPtr mpCamera; - ModelViewCameraController mCameraController; +private: + MegakernelPathTracer() = default; - bool mDirty = true; - static const Gui::DropdownList kModeList; - AlphaTestMode mAlphaTestMode = AlphaTestMode::HashedAlphaIsotropic; - float mHashScale = 1.0f; + bool init(const Dictionary& dict) override; + void recreateVars() override { mTracer.pVars = nullptr; } + void prepareVars(); + void setTracerData(const RenderData& renderData); - static const std::string skDefaultModel; + // Ray tracing program. + struct + { + RtState::SharedPtr pState; + RtProgram::SharedPtr pProgram; + RtProgramVars::SharedPtr pVars; + ParameterBlock::SharedPtr pParameterBlock; ///< ParameterBlock for all data. + } mTracer; }; diff --git a/Source/RenderPasses/PathTracer/Megakernel/PathTracer.rt.slang b/Source/RenderPasses/PathTracer/Megakernel/PathTracer.rt.slang new file mode 100644 index 000000000..bdceaf1db --- /dev/null +++ b/Source/RenderPasses/PathTracer/Megakernel/PathTracer.rt.slang @@ -0,0 +1,262 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Path tracing pass. + + This file contains the entry points for all ray tracing programs. + We import the path tracer utility functions defined in PathTracer.slang. + + The host sets the compile-time constants in StaticParams.slang. + It also sets the following defines for optional I/O buffers: + + is_valid_ is 1 if buffer with this name is bound, 0 otherwise. +*/ + +import PathTracer; +import Utils.Math.MathHelpers; +import RenderPasses.PathTracer.LoadGBuffer; + +shared ParameterBlock gData; + +// Outputs (optional) +shared RWTexture2D gOutputColor; +shared RWTexture2D gOutputAlbedo; +shared RWTexture2D gOutputDirect; +shared RWTexture2D gOutputIndirect; + +// Static configuration based on which buffers are bound. +#define isValid(name) (is_valid_##name != 0) +static const bool kComputeDirect = isValid(gOutputColor) || isValid(gOutputDirect); +static const bool kComputeIndirect = (isValid(gOutputColor) || isValid(gOutputIndirect)) && kMaxBounces > 0; + + +/** ********************* Ray index 0: Scatter ray ************************ */ + +[shader("miss")] +void scatterMiss(inout ScatterRayDataPacked packedData : SV_RayPayload) +{ + ScatterRayData rayData = packedData.unpack(); + handleMiss(gData, rayData); + packedData.pack(rayData); +} + +[shader("anyhit")] +void scatterAnyHit(inout ScatterRayDataPacked packedData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + // Alpha test for non-opaque geometry. + VertexData v = getVertexData(PrimitiveIndex(), attribs); + if (alphaTest(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], 0.f)) IgnoreHit(); +} + +[shader("closesthit")] +void scatterClosestHit(inout ScatterRayDataPacked packedData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + ScatterRayData rayData = packedData.unpack(); + + // Evaluate Falcor's material parameters at the hit point. + // Note we pass hitPos-rayDir as "camera position" to avoid zero-length rays causing NaNs + // in the view direction. It'd been cleaner if prepareShadingData() took ray dir directly. + // TODO: Implement texLOD to enable texture filtering in prepareShadingData(). + const float3 rayDir = WorldRayDirection(); + VertexData v = getVertexData(PrimitiveIndex(), attribs); + ShadingData sd = prepareShadingData(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], v.posW - rayDir, 0.f); + + // Compute tangent space if it is invalid. + if (!(dot(sd.B, sd.B) > 0.f)) // Note: Comparison written so that NaNs trigger + { + sd.B = perp_stark(sd.N); + sd.T = cross(sd.B, sd.N); + } + + // Prepare hit point struct with the additional data not part of ShadingData. + // These fields are needed for ray offset computatation and light PDF evaluation. + TriangleHit hit; + hit.meshInstanceID = getGlobalHitID(); + hit.primitiveIndex = PrimitiveIndex(); + hit.posW = sd.posW; + hit.normalW = sd.frontFacing ? sd.faceN : -sd.faceN; + hit.triangleArea = gScene.getFaceAreaW(hit.meshInstanceID, hit.primitiveIndex); + + handleHit(gData, sd, hit, kComputeIndirect, rayData); + packedData.pack(rayData); +} + +/************************** Ray index 1: Shadow ray ************************ */ + +[shader("miss")] +void shadowMiss(inout ShadowRayData rayData : SV_RayPayload) +{ + // The miss shader is executed if the ray misses all geometry. Mark as visible. + rayData.visible = true; +} + +[shader("anyhit")] +void shadowAnyHit(inout ShadowRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +{ + // Alpha test for non-opaque geometry. + VertexData v = getVertexData(PrimitiveIndex(), attribs); + if (alphaTest(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], 0.f)) IgnoreHit(); +} + +/** ******************************** RayGen ******************************** */ + +/** This is the entry point for the path tracer. + + We generate N paths (= #spp) per pixel, which are traced into the scene. + The path tracer is written as a for-loop over path segments, where each + iteration traces a shadow ray for direct illumination and a scatter ray. + + The hit shader for the scatter ray currently generates ray parameters for + the shadow ray to evaluate direct illumination and generates ray parameters + for the next scatter ray, which are both returned the raygen shader to be + traced. This is more efficient than tracing from the hit shader. The max + recusion depth = 1. +*/ +[shader("raygeneration")] +void rayGen() +{ + uint2 launchIndex = DispatchRaysIndex().xy; + uint2 launchDim = DispatchRaysDimensions().xy; + + float3 outDirect = float3(0, 0, 0); + float3 outIndirect = float3(0, 0, 0); + float3 outAlbedo = float3(0, 0, 0); + float outAlpha = 0.f; + + ShadingData sd; + if (loadShadingData(launchIndex, launchDim, gScene.camera, sd)) + { + // Pixel represents a primary hit. Compute its contribution. + + // Compute ray origin for new rays spawned from the G-buffer. + float3 rayOrigin = sd.computeNewRayOrigin(); + + // Loop over samples in pixel. + for (uint sampleIdx = 0; sampleIdx < kSamplesPerPixel; sampleIdx++) + { + // Create sample generator. + uint frameSeed = gData.params.useFixedSeed ? 0 : gData.params.frameCount; + SampleGenerator sg = SampleGenerator.create(launchIndex, frameSeed * kSamplesPerPixel + sampleIdx); + + // Advance the generator to the first available dimension. + // TODO: This is potentially expensive. We may want to store/restore the state from memory if it becomes a problem. + for (uint i = 0; i < gData.params.prngDimension; i++) sampleNext1D(sg); + + // Prepare ray payload. + ScatterRayData rayData = ScatterRayData.create(sg); + + float3 Ldirect = float3(0, 0, 0); + float3 Lindirect = float3(0, 0, 0); + + // Always output directly emitted light from the primary hit (unclamped as it's noise free). + // This is independent of whether emissive materials are treated as light sources or not. + outDirect += sd.emissive; + + // Sample direct illumination at primary hit. + if (kComputeDirect) + { + Ldirect += evalDirect(gData, sd, rayOrigin, rayData.sg); + } + + // Generate ray parameters for the first path segment. + generateScatterRay(gData, sd, rayOrigin, rayData); + + // Compute iteration count. + // This is statically determined based on the current configuration, is MIS/emissive is needed at the last bounce etc. + const uint kLastBounce = kComputeIndirect ? kMaxBounces : 0; + const uint kIterationCount = kTraceScatterRayFromLastPathVertex ? kLastBounce + 1 : kLastBounce; + + [unroll] + for (uint depth = 0; depth < kIterationCount && !rayData.terminated; depth++) + { + // Reset contributions. + rayData.Le = rayData.Lr = float3(0); + + // Trace scatter ray. The closest hit shader generates a shadow ray and a new scatter ray. + traceScatterRay(rayData); + + // Accumulate emitted radiance as direct/indirect depending on path length. + if (depth == 0) Ldirect += rayData.Le; + else Lindirect += rayData.Le; + + if (depth < kLastBounce) + { + // Trace shadow ray and accumulate reflected radiance as indirect if light is visible. + bool shadowValid = any(rayData.Lr > 0.f); + bool V = traceShadowRay(rayData.origin, rayData.shadowRay.xyz, rayData.shadowRay.w, shadowValid); + if (V) Lindirect += rayData.Lr; + + // Russian roulette to stochastically terminate the path. + // We use a fixed absorption probability for now. + // TODO: Better strategy, e.g., 1-P(absorption) = hemispherical reflectance of BRDF. + if (kUseRussianRoulette) + { + float u = sampleNext1D(rayData.sg); + if (u < gData.params.probabilityAbsorption) rayData.terminated = true; + rayData.thp /= (1.f - gData.params.probabilityAbsorption); + } + } + } + logPathLength(rayData.pathLength); + + // Accumulate direct/indirect illumination after clamping. + // Note the comparisons are purposely written so that NaNs propagate (unless the compiler rewrites it). + outDirect += gData.params.clampDirect && Ldirect > gData.params.thresholdDirect ? gData.params.thresholdDirect : Ldirect; + outIndirect += gData.params.clampIndirect && Lindirect > gData.params.thresholdIndirect ? gData.params.thresholdIndirect : Lindirect; + } + + // We're done accumulating over all samples. + const float invSpp = 1.f / kSamplesPerPixel; + outDirect *= invSpp; + outIndirect *= invSpp; + outAlbedo = sd.diffuse + sd.specular; + outAlpha = 1.f; + } + else + { + // Background pixel. + outDirect = evalBackground(gData.envProbe, -sd.V); + outIndirect = float3(0, 0, 0); + outAlpha = kForceAlphaOne ? 1.f : 0.f; + outAlbedo = outDirect.rgb; + } + + // Write outputs. + // These are all optional so using compile-time checks to decide which ones to write. + float3 outColor = outDirect + outIndirect; + assert(!any(isnan(outColor))); + + // DEBUG + //if (any(isnan(outColor))) outColor = float3(1, 0, 0); + //else if (any(isinf(outColor))) outColor = float3(0, 1, 0); + + if (isValid(gOutputColor)) gOutputColor[launchIndex] = float4(outColor, outAlpha); + if (isValid(gOutputDirect)) gOutputDirect[launchIndex] = float4(outDirect, outAlpha); + if (isValid(gOutputIndirect)) gOutputIndirect[launchIndex] = float4(outIndirect, outAlpha); + if (isValid(gOutputAlbedo)) gOutputAlbedo[launchIndex] = float4(outAlbedo, 1); +} diff --git a/Source/RenderPasses/PathTracer/Megakernel/PathTracer.slang b/Source/RenderPasses/PathTracer/Megakernel/PathTracer.slang new file mode 100644 index 000000000..f5e732459 --- /dev/null +++ b/Source/RenderPasses/PathTracer/Megakernel/PathTracer.slang @@ -0,0 +1,280 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Path tracer core functions. + These are called from the raygen, hit, and miss programs. + + To use it import PathTracer.slang and instantiate ParameterBlock, + which is passed in as the first argument to all functions that need it. + Any resources placed in PathTracerData are bound once and shared between all shaders/instances. +*/ + +#include "Utils/Math/MathConstants.slang" + +__exported import Scene; +__exported import Shading; +__exported import Raytracing; +__exported import RenderPasses.PathTracer.Logging; +__exported import RenderPasses.PathTracer.Megakernel.RayData; +__exported import Utils.Debug.PixelDebug; + +/** Shared path tracer data. + The ray tracing program instantiates this and passes it to all functions. +*/ +struct PathTracerData +{ + PathTracerParams params; ///< PathTracer shared parameters. + EnvProbe envProbe; ///< Environment map sampling functions. + EmissiveLightSampler emissiveSampler; ///< Emissive light sampler. +}; + +/** Traces a shadow ray towards a light source. + \param[in] origin Ray origin for the shadow ray. + \param[in] dir Direction from ray origin towards the light source (normalized). + \param[in] distance Distance to the light source. + \param[in] valid True if ray should be traced, false for dummy rays. + \return True if light is visible, false otherwise. +*/ +bool traceShadowRay(float3 origin, float3 dir, float distance, bool valid = true) +{ + // Setup ray descriptor. + RayDesc ray; + ray.Origin = origin; + ray.Direction = dir; + ray.TMin = 0.f; + ray.TMax = valid ? distance : 0.f; // Set tmax = 0.0 for dummy rays. + + ShadowRayData rayData; + rayData.visible = false; // Set to true by miss shader if ray is not terminated before + uint rayFlags = RAY_FLAG_SKIP_CLOSEST_HIT_SHADER | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH; + TraceRay(gRtScene, rayFlags, 0xff /* instanceInclusionMask */, kRayTypeShadow /* hitIdx */, hitProgramCount, kRayTypeShadow /* missIdx */, ray, rayData); + + // We let TraceRay() execute even when !valid in order to run the miss shader. + // This is faster on current drivers thanks to reduced divergence. + if (!valid) return false; + + // Note we're not counting stats for dummy rays. TODO: Make a separate bin in the logger. + logTraceRay(); + + return rayData.visible; +} + +/** Traces a scatter ray based on ray parameters stored in the ray payload. + \param[in,out] rayData Describes the ray parameters. The struct is modified based on the result. +*/ +void traceScatterRay(inout ScatterRayData rayData) +{ + // Setup ray based on params passed via payload. + RayDesc ray; + ray.Origin = rayData.origin; + ray.Direction = rayData.direction; + ray.TMin = 0.f; + ray.TMax = kRayTMax; + + // Pack ray payload. + ScatterRayDataPacked packedData; + packedData.pack(rayData); + uint rayFlags = 0; // TODO: Set cull mode from the app + TraceRay(gRtScene, rayFlags, 0xff /* instanceInclusionMask */, kRayTypeScatter /* hitIdx */, hitProgramCount, kRayTypeScatter /* missIdx */, ray, packedData); + + logTraceRay(); + + // Unpack ray payload. + rayData = packedData.unpack(); +} + +/** Evaluates direct illumination at hit point. + \param[in] pt PathTracer data. + \param[in] sd Shading data. + \param[in] rayOrigin Ray origin for the new ray. + \param[in,out] sg SampleGenerator object. + \return Outgoing radiance in view direction due to direct illumination. +*/ +float3 evalDirect(const PathTracerData pt, const ShadingData sd, const float3 rayOrigin, inout SampleGenerator sg) +{ + // Draw a light sample. This chooses stochastically between all enabled lights in the scene. + SceneLightSample ls = sampleSceneLights(pt.params, pt.envProbe, pt.emissiveSampler, sd, rayOrigin, sg); + const bool valid = any(ls.Li > 0.f); + + // Test visibility by tracing a shadow ray. + bool V = traceShadowRay(rayOrigin, ls.rayDir, ls.rayDistance, valid); + return V ? evalBRDFCosine(sd, ls.dir) * ls.Li : float3(0); +} + +/** Processes a hit point to generate a shadow ray for sampling the light sources. + This should be called before generateScatterRay() as the latter updates the path throughput. + \param[in] pt PathTracer data. + \param[in] sd Shading data. + \param[in] rayOrigin Ray origin for the new ray. + \param[in,out] rayData Ray payload. +*/ +void generateShadowRay(const PathTracerData pt, const ShadingData sd, const float3 rayOrigin, inout ScatterRayData rayData) +{ + // Sample the scene lights. + SceneLightSample ls = sampleSceneLights(pt.params, pt.envProbe, pt.emissiveSampler, sd, rayOrigin, rayData.sg); + const bool valid = any(ls.Li > 0.f); + + // Return ray parameters and incident radiance, weighted by path throughput. + rayData.shadowRay = float4(ls.rayDir, ls.rayDistance); + rayData.Lr = valid ? evalBRDFCosine(sd, ls.dir) * ls.Li * rayData.thp : float3(0); +} + +/** Processes a hit point to generate a scatter ray or terminate. + The function uses BRDF sampling to generate ray parameters for the scatter ray. + It's called from the raygen shader for primary hits and from the closest hit shader for secondary hits. + \param[in] pt PathTracer data. + \param[in] sd Shading data. + \param[in] rayOrigin Ray origin for the new ray. + \param[in,out] rayData Ray payload. +*/ +void generateScatterRay(const PathTracerData pt, const ShadingData sd, const float3 rayOrigin, inout ScatterRayData rayData) +{ + // Generate next path segment. + rayData.origin = rayOrigin; + if (kUseBRDFSampling) + { + // Default path that uses BRDF importance sampling. + BRDFSample result; + sampleBRDF(sd, rayData.sg, result); + + rayData.direction = result.dir; + rayData.thp *= result.thp; + + // Note that the pdf can be costly, so we store it in the payload if we need it (MIS enabled), + // and rely on dead code elimination to remove the computations otherwise. + rayData.pdf = kUseMIS ? result.pdf : 0.f; + } + else + { + // Fallback path that uses cosine-weighted hemisphere sampling. + float pdf; + float3 dir = sampleHemisphereCosine(sd, sampleNext2D(rayData.sg), pdf); // pdf = cos(theta) / pi + + // Check that L and V are in the positive hemisphere, reset pdf to zero otherwise. + // This is necessary for consistency with BRDF sampling. + float NdotL = dot(sd.N, dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) pdf = 0.f; + + rayData.direction = dir; + rayData.thp *= evalBRDF(sd, dir) * M_PI; // dot(N,L) / pdf = pi + rayData.pdf = kUseMIS ? pdf : 0.f; + } + + assert(!any(isnan(rayData.thp))); + assert(!isnan(rayData.pdf)); + + // Pass on the shading normal. Only store it if needed (MIS enabled). + if (kUseMIS) rayData.normal = sd.N; + + // Terminate if ray throughput is zero. + // This may happen if the sample is invalid, and consequently the BRDF sampling function returns thp == 0.0. + if (all(rayData.thp == 0.f)) rayData.terminated = true; +} + +/** Handles the case when a path misses the scene. + Depending on the configuration, we add the env map contribution here using MIS. + \param[in] pt PathTracer data. + \param[in,out] rayData Ray payload. +*/ +void handleMiss(const PathTracerData pt, inout ScatterRayData rayData) +{ + // Ray missed the scene. Mark the path as terminated. + rayData.terminated = true; + + // If we have an environment map and MIS is enable, add the weighted contribution here. + if (kUseEnvLight && kUseMIS) + { + // We came here through BRDF sampling. The other sampling strategy is + // env map sampling. Evaluate it's probability for the current ray dir. + const float3 dir = rayData.direction; + float lightPdf = evalEnvProbePdf(pt.envProbe, dir) * getEnvLightSelectionPdf(); + + // Compute MIS weighted contribution from the environment map. + float misWeight = evalMIS(pt.params, rayData.pdf, lightPdf); + float3 Le = evalEnvProbe(pt.envProbe, dir) * misWeight; + + // Return emitted radiance weighted by path throughput. + rayData.Le = rayData.thp * Le; + } +} + +/** Handles the case when a path hits a surface. + Depending on the configuration, we add the emissive contribution here using MIS. + Then direct illumination is sampled and the next segment of the path generated. + \param[in] pt PathTracer data. + \param[in] sd Shading data. + \param[in] hit Triangle hit data (for EmissiveLightSampler). + \param[in] computeIndirect True if indirect illumination is enabled. + \param[in,out] rayData Ray payload. +*/ +void handleHit(const PathTracerData pt, const ShadingData sd, const TriangleHit hit, const bool computeIndirect, inout ScatterRayData rayData) +{ + // Statically determine if we need to compute the emissive based on the current configuration. + // It's only needed if emissive is enabled, and its full contribution hasn't been sampled elsewhere. + const bool computeEmissive = kUseEmissiveLights && (!kUseEmissiveSampler || (kUseEmissiveSampler && kUseMIS)); + + if (computeEmissive && any(sd.emissive > 0.f)) + { + float misWeight = 1.f; + if (kUseEmissiveSampler && kUseMIS) + { + // If emissive light sampling and MIS is enabled, we've already sampled emissive lights using NEE. + // We need to evaluate the MIS weight here to account for the remaining contribution. + + // Evaluate PDF at the hit, had it been generated with light sampling. + // Note that there is a minor discrepancy compared to BRDF sampling at the previous path vertex: + // the normal passed through the ray payload is lossily compressed to reduce the ray payload size. + float lightPdf = pt.emissiveSampler.evalPdf(rayData.origin, rayData.normal, hit) * getEmissiveLightSelectionPdf(); + + // Compute MIS weight by combining this with BRDF sampling. + // Note we can assume rayData.pdf > 0.f since we shouldn't have got here otherwise. + misWeight = evalMIS(pt.params, rayData.pdf, lightPdf); + } + + // Return emitted radiance weighted by path throughput and MIS weight. + rayData.Le = rayData.thp * sd.emissive * misWeight; + } + + // Unless path ends here, sample light and generate next path segment. + // TODO: We should try running a different shader for the last path segment to see if that's faster than branching out here. + if (computeIndirect && rayData.pathLength < kMaxBounces) + { + // Compute ray origin for new rays spawned from the hit. + const float3 rayOrigin = sd.computeNewRayOrigin(); + + // Generate shadow ray for sampling the scene lights. + // The raygen shader will trace the shadow ray and accumulate the contribution. + generateShadowRay(pt, sd, rayOrigin, rayData); + + // Generate the ray parameters for the next path segment. + // If path is not terminated, the raygen shader will continue the path based on the returned payload. + generateScatterRay(pt, sd, rayOrigin, rayData); + + rayData.pathLength++; + } +} diff --git a/Source/RenderPasses/PathTracer/Megakernel/RayData.slang b/Source/RenderPasses/PathTracer/Megakernel/RayData.slang new file mode 100644 index 000000000..7652807ef --- /dev/null +++ b/Source/RenderPasses/PathTracer/Megakernel/RayData.slang @@ -0,0 +1,161 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Declarations of the ray payload structs. + + We have utility functions to pack/unpack the payload to save registers + and increase performance. +*/ + +import Utils.Math.MathHelpers; +import Utils.Math.FormatConversion; +__exported import Utils.Sampling.SampleGenerator; +__exported import RenderPasses.PathTracer.PathTracerHelpers; + +/** Payload for shadow ray. +*/ +struct ShadowRayData +{ + bool visible; +}; + +/** Unpacked payload for scatter ray. +*/ +struct ScatterRayData +{ + bool terminated; ///< Set to true when path is terminated. + uint pathLength; ///< Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). + + float3 Le; ///< Emitted radiance at the last path vertex, weighted by path throughput and MIS weight. + float3 Lr; ///< Reflected radiance from the sampled light at the last path vertex, weighted by path throughput, BRDF, and MIS weight. + + float3 origin; ///< Next path segment origin (unless path is terminated). + float3 direction; ///< Next path segment direction (unless path is terminated). + + float3 thp; ///< Current path throughput. This is updated at each path vertex. + + float3 normal; ///< Shading normal at the ray origin. Only used if MIS is enabled. + float pdf; ///< Probability density function with respect to solid angle at the last path vertex. Only used if MIS is enabled. + + float4 shadowRay; ///< Shadow ray direction (xyz) and distance (w). Only used if Lr > 0. + + SampleGenerator sg; ///< Per-ray state for the sample generator (up to 16B). + + /** Create ray payload with default parameters. + */ + static ScatterRayData create(SampleGenerator sg) + { + ScatterRayData d; + d.terminated = false; + d.pathLength = 0; + d.Le = float3(0, 0, 0); + d.Lr = float3(0, 0, 0); + d.origin = float3(0, 0, 0); + d.direction = float3(0, 0, 0); + d.thp = float3(1, 1, 1); + d.normal = float3(0, 0, 0); + d.pdf = 0.f; + d.shadowRay = float4(0, 0, 0, 0); + d.sg = sg; + + return d; + } +}; + +/** Packed payload for scatter ray. + + For performance reasons it's recommended to keep the payload as small as possible. + We store the payload in packed format, and have helper function to pack/unpack it. + Note that we use a compressed format for the ray direction (16-bit precision). +*/ +struct ScatterRayDataPacked +{ + float4 packedData[5]; ///< Packed payload (80B). + SampleGenerator sg; ///< Sample generator state (up to 16B). + + /** Pack payload. + \param[in] data The unpacked payload data to be stored in the struct. + */ + [mutating] void pack(ScatterRayData data) + { + // Pack direction as 3x 16-bit snorm and flags/length in remaining 16 bits. + // TODO: We could convert the direction to spherical coordinates first and pack as 2x 16-bit values, but avoid if necessary (precision/perf loss). + uint flags = ((data.pathLength & kMaxPathLength) << 1) | (data.terminated ? 1 : 0); + uint packed0 = (flags << 16) | packSnorm16(data.direction.x); + uint packed1 = packSnorm2x16(data.direction.yz); + + // Compress shading normal as 2x 16-bit snorms values in the octahedral mapping. + // Note that there is some loss which we should adjust for where the normal is used. + float2 octNormal = ndir_to_oct_snorm(data.normal); + uint packed2 = packSnorm2x16(octNormal); + + packedData[0].xyz = data.Le; + packedData[0].w = asfloat(packed0); + packedData[1].xyz = data.Lr; + packedData[1].w = asfloat(packed1); + packedData[2].xyz = data.origin; + packedData[2].w = data.pdf; + packedData[3].xyz = data.thp; + packedData[3].w = asfloat(packed2); + packedData[4].xyzw = data.shadowRay; + sg = data.sg; + } + + /** Unpack payload. + \return The unpacked payload data. + */ + ScatterRayData unpack() + { + ScatterRayData rayData; + + uint packed0 = asuint(packedData[0].w); + uint packed1 = asuint(packedData[1].w); + uint flags = (packed0 >> 16); + + // Decompress shading normal. + uint packed2 = asuint(packedData[3].w); + float2 octNormal = unpackSnorm2x16(packed2); + float3 normal = oct_to_ndir_snorm(octNormal); + + rayData.terminated = (flags & 1) != 0; + rayData.pathLength = (flags >> 1) & kMaxPathLength; + rayData.direction.x = unpackSnorm16(packed0); + rayData.direction.yz = unpackSnorm2x16(packed1); + rayData.direction = normalize(rayData.direction); + rayData.Le = packedData[0].xyz; + rayData.Lr = packedData[1].xyz; + rayData.origin = packedData[2].xyz; + rayData.pdf = packedData[2].w; + rayData.thp = packedData[3].xyz; + rayData.normal = normal; + rayData.shadowRay = packedData[4].xyzw; + rayData.sg = sg; + + return rayData; + } +}; diff --git a/Source/RenderPasses/PathTracer/PathData.slang b/Source/RenderPasses/PathTracer/PathData.slang new file mode 100644 index 000000000..f0bfd865d --- /dev/null +++ b/Source/RenderPasses/PathTracer/PathData.slang @@ -0,0 +1,125 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +__exported import Utils.Sampling.SampleGenerator; +__exported import RenderPasses.PathTracer.StaticParams; + +static const uint kInvalidIndex = 0xffffffff; + +/** Ray hit information. This uniquely identifies a hit point. +*/ +struct HitInfo +{ + uint meshInstanceID; ///< Mesh instance ID for ray hit, or kInvalidIndex if miss. + uint primitiveIndex; ///< Primitive index for ray hit, undefined otherwise. + float2 barycentrics; ///< Barycentric coordinates at the hit, undefined otherwise. + + [mutating] void clear() + { + meshInstanceID = kInvalidIndex; + primitiveIndex = kInvalidIndex; + barycentrics = float2(0, 0); + } +}; + +struct ShadowRay +{ + float4 rayParams; ///< Shadow ray normalized direction (xyz) and distance (w). + float3 Lr; ///< Unoccluded contribution from the shadow ray (xyz). + float _pad; +}; + +enum class PathFlags +{ + terminated = 0x000001, ///< Path is terminated. There are no more operations left to do. + scatterRay = 0x000002, ///< Scatter ray is active. + scatterHit = 0x000004, ///< Result of the scatter ray (0 = miss, 1 = hit). + // Bit 3 reserved + + // Bits 4-13 shadow ray active + // Bits 14-24 shadow test result + shadowRay = 0x000010, ///< Shadow ray is active. + shadowResult = 0x004000, ///< Result of the shadow test (0 = occluded, 1 = visible). + + anyShadowRays = 0x003ff0, ///< Bit mask to test if there are any active shadow rays. + anyRays = 0x003ff2, ///< Bit mask to test if there are any active rays. + shadowRayBits = 0xfffff0, +}; + +/** Working data for the path tracer. + + Note that the shadow ray data is handled separately to avoid having a very + large live state when there are multiple light samples per vertex. +*/ +struct PathData +{ + uint flags; ///< Flags indicating the current status. This can be multiple PathFlags flags OR'ed together. + uint length; ///< Path length (0 at origin, 1 at first secondary hit, etc.). + + // Scatter ray + float3 origin; ///< Origin of the shadow/scatter rays. Note: This field is only loaded if MIS is enabled. + float3 dir; ///< Scatter ray normalized direction. + float3 thp; ///< Path throughput. + float pdf; ///< Pdf for generating the scatter ray. Note: This field is only loaded/stored if MIS is enabled. + float3 normal; ///< Shading normal at the scatter ray origin. Note: This field is only loaded/stored if MIS is enabled. + HitInfo hit; ///< Hit information for the scatter ray. This is populated by the tracing pass. Only valid if the flag 'scatterHit' is set. + + // Common data + float3 Ldirect; ///< Accumulated radiance due to direct illumination. + float3 Lindirect; ///< Accumulated radiance due to indirect illumination. + + SampleGenerator sg; ///< Sample generator state. Note: This is only valid when path.length < kMaxBounces. + + + // Utility functions + + bool isTerminated() { return (flags & uint(PathFlags::terminated)) != 0; } + bool hasScatterRay() { return (flags & uint(PathFlags::scatterRay)) != 0; } + bool hasShadowRay(uint i) { return (flags & (uint(PathFlags::shadowRay) << i)) != 0; } + bool hasRays() { return (flags & uint(PathFlags::anyRays)) != 0; } + + [mutating] void clearScatterRay() { flags &= ~(uint(PathFlags::scatterRay) | uint(PathFlags::scatterHit)); } + [mutating] void clearShadowRay(uint i) { flags &= ~((uint(PathFlags::shadowRay) | uint(PathFlags::shadowResult)) << i); } + [mutating] void clearShadowRays() { flags &= ~(uint(PathFlags::shadowRayBits)); } + + /** Clear all members to their defaults. + */ + [mutating] void clear() + { + flags = 0; + length = 0; + origin = float3(0); + dir = float3(0); + thp = float3(0); + pdf = 0; + normal = float3(0); + hit.clear(); + Ldirect = float3(0); + Lindirect = float3(0); + sg = {}; + } +}; diff --git a/Source/RenderPasses/PathTracer/PathTracer.cpp b/Source/RenderPasses/PathTracer/PathTracer.cpp new file mode 100644 index 000000000..58b9c9cb6 --- /dev/null +++ b/Source/RenderPasses/PathTracer/PathTracer.cpp @@ -0,0 +1,642 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "PathTracer.h" +#include "Megakernel/MegakernelPathTracer.h" +#include + +static void regPathTracer(Falcor::ScriptBindings::Module& m) +{ + // Register our parameters struct. + auto params = m.regClass(PathTracerParams); +#define field(f_) rwField(#f_, &PathTracerParams::f_) + // General + params.field(samplesPerPixel); + params.field(lightSamplesPerVertex); + params.field(maxBounces); + params.field(forceAlphaOne); + + params.field(clampDirect); + params.field(clampIndirect); + params.field(thresholdDirect); + params.field(thresholdIndirect); + + // Lighting + params.field(useAnalyticLights); + params.field(useEmissiveLights); + params.field(useEnvLight); + params.field(useEnvBackground); + + // Sampling + params.field(useBRDFSampling); + params.field(useMIS); + params.field(misHeuristic); + params.field(misPowerExponent); + + params.field(useEmissiveLightSampling); + params.field(useRussianRoulette); + params.field(probabilityAbsorption); + params.field(useFixedSeed); +#undef field + + // Register script bindings for utils in static libs since they can't do it themselves. + // Note if this is called multiple times we'll get a warning. + // TODO: Better solution for script bindings in FalcorInternal. + // TODO: Use m.classExists() to check first, or do that inside the function? + EmissiveLightSampler::registerScriptBindings(m); + EmissiveUniformSampler::registerScriptBindings(m); +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("MegakernelPathTracer", MegakernelPathTracer::sDesc, MegakernelPathTracer::create); + Falcor::ScriptBindings::registerBinding(regPathTracer); +} + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +// Declare the names of channels that we need direct access to. The rest are bound in bulk based on the lists below. +// TODO: Figure out a cleaner design for this. Should we have enums for all the channels? +const std::string PathTracer::kViewDirInput = "viewW"; +const std::string PathTracer::kAlbedoOutput = "albedo"; + +const Falcor::ChannelList PathTracer::kInputChannels = +{ + { "posW", "gWorldPosition", "World-space position (xyz) and foreground flag (w)" }, + { "normalW", "gWorldShadingNormal", "World-space shading normal (xyz)" }, + { "bitangentW", "gWorldShadingBitangent", "World-space shading bitangent (xyz)", true /* optional */ }, + { "faceNormalW", "gWorldFaceNormal", "Face normal in world space (xyz)", }, + { kViewDirInput, "gWorldView", "World-space view direction (xyz)", true /* optional */ }, + { "mtlDiffOpacity", "gMaterialDiffuseOpacity", "Material diffuse color (xyz) and opacity (w)" }, + { "mtlSpecRough", "gMaterialSpecularRoughness", "Material specular color (xyz) and roughness (w)" }, + { "mtlEmissive", "gMaterialEmissive", "Material emissive color (xyz)" }, + { "mtlParams", "gMaterialExtraParams", "Material parameters (IoR, flags etc)" }, +}; + +const Falcor::ChannelList PathTracer::kOutputChannels = +{ + { "color", "gOutputColor", "Output color (sum of direct and indirect)", true /* optional */ }, + { kAlbedoOutput, "gOutputAlbedo", "Surface albedo (base color) or background color", true /* optional */ }, + { "direct", "gOutputDirect", "Direct illumination (linear)", true /* optional */ }, + { "indirect", "gOutputIndirect", "Indirect illumination (linear)", true /* optional */ }, + { "rayCount", "", "Per-pixel ray count", true /* optional */, ResourceFormat::R32Uint }, +}; + +namespace +{ + // UI variables. + const Gui::DropdownList kSampleGeneratorList = + { + { SAMPLE_GENERATOR_UNIFORM, "Uniform (128-bit)" }, + { SAMPLE_GENERATOR_TINY_UNIFORM, "Tiny uniform (32-bit)" }, + }; + + const Gui::DropdownList kMISHeuristicList = + { + { (uint32_t)MISHeuristic::BalanceHeuristic, "Balance heuristic" }, + { (uint32_t)MISHeuristic::PowerTwoHeuristic, "Power heuristic (exp=2)" }, + { (uint32_t)MISHeuristic::PowerExpHeuristic, "Power heuristic" }, + }; + + const Gui::DropdownList kEmissiveSamplerList = + { + { (uint32_t)EmissiveLightSamplerType::Uniform, "Uniform" }, + }; +}; + +static_assert(has_vtable::value == false, "PathTracerParams must be non-virtual"); +static_assert(sizeof(PathTracerParams) % 16 == 0, "PathTracerParams size should be a multiple of 16"); +static_assert(kMaxPathLength > 0 && ((kMaxPathLength & (kMaxPathLength + 1)) == 0), "kMaxPathLength should be 2^N-1"); +static_assert(kMaxPathLengthBits <= 8, "kMaxPathLength should be 255 or smaller"); + +bool PathTracer::init(const Dictionary& dict) +{ + // Deserialize pass from dictionary. + serializePass(dict); + validateParameters(); + + // Create a sample generator. + mpSampleGenerator = SampleGenerator::create(mSelectedSampleGenerator); + assert(mpSampleGenerator); + + // Stats and debugging utils. + mStatsLogger = Logging::create(); + assert(mStatsLogger); + mPixelDebugger = PixelDebug::create(); + assert(mPixelDebugger); + + return true; +} + +Dictionary PathTracer::getScriptingDictionary() +{ + Dictionary dict; + serializePass(dict); + return dict; +} + +RenderPassReflection PathTracer::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + + // Define our input/output channels. + for (auto it : kInputChannels) + { + auto& buf = reflector.addInput(it.name, it.desc); + buf.bindFlags(ResourceBindFlags::ShaderResource); + buf.format(it.format); + if (it.optional) buf.flags(RenderPassReflection::Field::Flags::Optional); + } + for (auto it : kOutputChannels) + { + auto& buf = reflector.addOutput(it.name, it.desc); + buf.bindFlags(ResourceBindFlags::UnorderedAccess); + buf.format(it.format); + if (it.optional) buf.flags(RenderPassReflection::Field::Flags::Optional); + } + + return reflector; +} + +void PathTracer::compile(RenderContext* pRenderContext, const CompileData& compileData) +{ + mSharedParams.frameDim = compileData.defaultTexDims; +} + +void PathTracer::renderUI(Gui::Widgets& widget) +{ + bool dirty = false; + + dirty |= widget.var("Samples/pixel", mSharedParams.samplesPerPixel, 1u, 1u << 16, 1); + if (dirty |= widget.var("Light samples/vertex", mSharedParams.lightSamplesPerVertex, 1u, kMaxLightSamplesPerVertex)) recreateVars(); // Trigger recreation of the program vars. + widget.tooltip("The number of shadow rays that will be traced at each path vertex.\n" + "The supported range is [1," + std::to_string(kMaxLightSamplesPerVertex) + "].", true); + dirty |= widget.var("Max bounces", mSharedParams.maxBounces, 0u, kMaxPathLength); + widget.tooltip("Maximum path length.\n0 = direct only\n1 = one indirect bounce etc.", true); + + widget.text("Max rays/pixel: " + std::to_string(mMaxRaysPerPixel)); + widget.tooltip("This is the maximum number of rays that will be traced per pixel.\n" + "The number depends on the scene's available light types and the current configuration.", true); + + // Clamping for basic firefly removal. + dirty |= widget.checkbox("Clamp direct", mSharedParams.clampDirect); + widget.dummy("##spacing0", { 10,1 }, true); + dirty |= widget.checkbox("Clamp indirect", mSharedParams.clampIndirect, true); + widget.tooltip("Basic firefly removal.\nThese options enable per-sample clamping of direct/indirect illumination before accumulating.\nNote that energy is lost and the images will be darker when clamping is enabled.", true); + if (mSharedParams.clampDirect) + { + dirty |= widget.var("Direct threshold", mSharedParams.thresholdDirect, 0.f, std::numeric_limits::max(), mSharedParams.thresholdDirect * 0.01f); + } + if (mSharedParams.clampIndirect) + { + dirty |= widget.var("Indirect threshold", mSharedParams.thresholdIndirect, 0.f, std::numeric_limits::max(), mSharedParams.thresholdIndirect * 0.01f); + } + + dirty |= widget.checkbox("Force alpha to 1.0", mSharedParams.forceAlphaOne); + widget.tooltip("Forces the output alpha channel to 1.0.\n" + "Otherwise the background will be 0.0 and the foreground 1.0 to allow separate compositing.", true); + + // Draw sub-groups for various options. + dirty |= renderSamplingUI(widget); + dirty |= renderLightsUI(widget); + renderLoggingUI(widget); + + // If rendering options that modify the output have changed, set flag to indicate that. + // In execute() we will pass the flag to other passes for reset of temporal data etc. + if (dirty) + { + validateParameters(); + mOptionsChanged = true; + } +} + +bool PathTracer::renderSamplingUI(Gui::Widgets& widget) +{ + bool dirty = false; + + auto samplingGroup = Gui::Group(widget, "Sampling", true); + if (samplingGroup.open()) + { + // Importance sampling controls. + dirty |= samplingGroup.checkbox("BRDF importance sampling", mSharedParams.useBRDFSampling); + samplingGroup.tooltip("BRDF importance sampling should normally be enabled.\n\n" + "If disabled, cosine-weighted hemisphere sampling is used.\n" + "That can be useful for debugging but expect slow convergence.", true); + + dirty |= samplingGroup.checkbox("Multiple importance sampling (MIS)", mSharedParams.useMIS); + samplingGroup.tooltip("MIS should normally be enabled.\n\n" + "BRDF sampling is combined with light sampling for the environment map and emissive lights.\n" + "Note that MIS has currently no effect on analytic lights.", true); + if (mSharedParams.useMIS) + { + dirty |= samplingGroup.dropdown("MIS heuristic", kMISHeuristicList, mSharedParams.misHeuristic); + if (mSharedParams.misHeuristic == (uint32_t)MISHeuristic::PowerExpHeuristic) + { + dirty |= samplingGroup.var("MIS power exponent", mSharedParams.misPowerExponent, 0.01f, 10.f); + } + } + + // Russian roulette. + dirty |= samplingGroup.checkbox("Russian roulette", mSharedParams.useRussianRoulette); + if (mSharedParams.useRussianRoulette) + { + dirty |= samplingGroup.var("Absorption probability ", mSharedParams.probabilityAbsorption, 0.0f, 0.999f); + samplingGroup.tooltip("Russian roulette probability of absorption at each bounce (p).\n" + "Disable via the checkbox if not used (setting p = 0.0 still incurs a runtime cost).", true); + } + + // Sample generator selection. + samplingGroup.text("Sample generator:"); + if (samplingGroup.dropdown("##SampleGenerator", kSampleGeneratorList, mSelectedSampleGenerator, true)) + { + mpSampleGenerator = SampleGenerator::create(mSelectedSampleGenerator); + if (!mpSampleGenerator) throw std::exception("Failed to create sample generator"); + recreateVars(); // Trigger recreation of the program vars. + dirty = true; + } + + samplingGroup.checkbox("Use fixed seed", mSharedParams.useFixedSeed); + samplingGroup.tooltip("Forces a fixed random seed for each frame.\n\n" + "This should produce exactly the same image each frame, which can be useful for debugging using print() and otherwise.", true); + + samplingGroup.release(); + } + + return dirty; +} + +bool PathTracer::renderLightsUI(Gui::Widgets& widget) +{ + bool dirty = false; + + auto lightsGroup = Gui::Group(widget, "Lights", true); + if (lightsGroup.open()) + { + dirty |= lightsGroup.checkbox("Use analytic lights", mSharedParams.useAnalyticLights); + lightsGroup.tooltip("This enables Falcor's built-in analytic lights.\nThese are specified in the scene description (.fscene).", true); + + dirty |= lightsGroup.checkbox("Use emissive lights", mSharedParams.useEmissiveLights); + lightsGroup.tooltip("This enables using emissive triangles as light sources.", true); + if (mSharedParams.useEmissiveLights) + { + dirty |= lightsGroup.checkbox("Use emissive light sampling", mSharedParams.useEmissiveLightSampling); + lightsGroup.tooltip("This option enables explicit sampling of emissive geometry by using an emissive sampler to pick samples " + "on the emissive triangles and tracing shadow rays to evaluate their visibility. See options in separate tab.\n" + "When disabled, the contribution from emissive lights is only accounted for when they are directly hit by a scatter ray.", true); + + lightsGroup.text("Emissive sampler:"); + lightsGroup.tooltip("Selects which light sampler to use for importance sampling of emissive geometry.", true); + if (lightsGroup.dropdown("##EmissiveSampler", kEmissiveSamplerList, (uint32_t&)mSelectedEmissiveSampler, true)) + { + mpEmissiveSampler = nullptr; + dirty = true; + } + } + + dirty |= lightsGroup.checkbox("Use env map as light", mSharedParams.useEnvLight); + lightsGroup.tooltip("This enables using the environment map as a distant light source", true); + dirty |= lightsGroup.checkbox("Use env map as background", mSharedParams.useEnvBackground); + + // Print info about the lights. + std::ostringstream oss; + oss << "Analytic lights: " + << mSharedParams.lightCountPoint << " point, " + << mSharedParams.lightCountDirectional << " directional, " + << mSharedParams.lightCountAnalyticArea << " area\n" + << "Mesh lights: "; + if (mpEmissiveSampler) + { + oss << mpEmissiveSampler->getLightCount() << " triangles"; + } + else + { + oss << "info not available"; // TODO: Add helper on Scene that computes the total number of emissive triangles. + } + lightsGroup.text(oss.str()); + + lightsGroup.text("Environment map: " + (mpEnvProbe ? mEnvProbeFilename : "N/A")); + + lightsGroup.release(); + } + + if (mpEmissiveSampler) + { + auto emissiveGroup = Gui::Group(widget, "Emissive sampler options"); + if (emissiveGroup.open()) + { + if (mpEmissiveSampler->renderUI(emissiveGroup)) + { + // Get the latest options for the current sampler. We need these to re-create the sampler at scene changes and for pass serialization. + switch (mSelectedEmissiveSampler) + { + case EmissiveLightSamplerType::Uniform: + mUniformSamplerOptions = std::static_pointer_cast(mpEmissiveSampler)->getOptions(); + break; + default: + should_not_get_here(); + } + dirty = true; + } + + emissiveGroup.release(); + } + } + + return dirty; +} + +void PathTracer::renderLoggingUI(Gui::Widgets& widget) +{ + auto logGroup = Gui::Group(widget, "Logging"); + if (logGroup.open()) + { + // Traversal stats. + mStatsLogger->renderUI(logGroup); + + // Pixel debugger. + mPixelDebugger->renderUI(logGroup); + + logGroup.release(); + } +} + +void PathTracer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + mpScene = pScene; + mSharedParams.frameCount = 0; + + // Lighting setup. This clears previous data if no scene is given. + if (!initLights(pRenderContext)) throw std::exception("Failed to initialize lights"); + + recreateVars(); // Trigger recreation of the program vars. +} + +bool PathTracer::onMouseEvent(const MouseEvent& mouseEvent) +{ + return mPixelDebugger->onMouseEvent(mouseEvent); +} + +void PathTracer::validateParameters() +{ + if (mSharedParams.lightSamplesPerVertex < 1 || mSharedParams.lightSamplesPerVertex > kMaxLightSamplesPerVertex) + { + logWarning("Unsupported number of light samples per path vertex. Clamping to the range [1," + std::to_string(kMaxLightSamplesPerVertex) + "]."); + mSharedParams.lightSamplesPerVertex = std::clamp(mSharedParams.lightSamplesPerVertex, 1u, kMaxLightSamplesPerVertex); + recreateVars(); + } + + if (mSharedParams.maxBounces > kMaxPathLength) + { + logWarning("'maxBounces' exceeds the maximum supported path length. Clamping to " + std::to_string(kMaxPathLength)); + mSharedParams.maxBounces = kMaxPathLength; + } +} + +bool PathTracer::initLights(RenderContext* pRenderContext) +{ + // Clear lighting data for previous scene. + mpEnvProbe = nullptr; + mEnvProbeFilename = ""; + mpEmissiveSampler = nullptr; + mUseEmissiveLights = mUseEmissiveSampler = mUseAnalyticLights = mUseEnvLight = false; + mSharedParams.lightCountPoint = 0; + mSharedParams.lightCountDirectional = 0; + mSharedParams.lightCountAnalyticArea = 0; + + // If we have no scene, we're done. + if (mpScene == nullptr) return true; + + // Load environment map if scene uses one. + // We're getting the file name from the scene's LightProbe because that was used in the fscene files. + // TODO: Switch to use Scene::getEnvironmentMap() when the assets have been updated. + auto pLightProbe = mpScene->getLightProbe(); + if (pLightProbe != nullptr) + { + std::string fn = pLightProbe->getOrigTexture()->getSourceFilename(); + mpEnvProbe = EnvProbe::create(pRenderContext, fn); + mEnvProbeFilename = mpEnvProbe ? getFilenameFromPath(mpEnvProbe->getEnvMap()->getSourceFilename()) : ""; + } + + // Setup for analytic lights. + for (uint32_t i = 0; i < mpScene->getLightCount(); i++) + { + switch (mpScene->getLight(i)->getType()) + { + case LightPoint: + mSharedParams.lightCountPoint++; + break; + case LightDirectional: + mSharedParams.lightCountDirectional++; + break; + case LightAreaRect: + case LightAreaSphere: + case LightAreaDisc: + mSharedParams.lightCountAnalyticArea++; + break; + case LightArea: + default: + logError("Scene has invalid light types. Aborting."); + return false; + } + } + + return true; +} + +bool PathTracer::updateLights(RenderContext* pRenderContext) +{ + // If no scene is loaded, we disable everything. + if (!mpScene) + { + mUseAnalyticLights = false; + mUseEnvLight = false; + mUseEmissiveLights = mUseEmissiveSampler = false; + mpEmissiveSampler = nullptr; + return false; + } + + // Configure light sampling. + mUseAnalyticLights = mSharedParams.useAnalyticLights && mpScene->getLightCount() > 0; + mUseEnvLight = mSharedParams.useEnvLight && mpEnvProbe != nullptr; + + bool lightingChanged = false; + if (!mSharedParams.useEmissiveLights) + { + mUseEmissiveLights = mUseEmissiveSampler = false; + mpEmissiveSampler = nullptr; + } + else + { + mUseEmissiveLights = true; + mUseEmissiveSampler = mSharedParams.useEmissiveLightSampling; + + if (!mUseEmissiveSampler) + { + mpEmissiveSampler = nullptr; + } + else + { + // Create emissive light sampler if it doesn't already exist. + if (mpEmissiveSampler == nullptr) + { + switch (mSelectedEmissiveSampler) + { + case EmissiveLightSamplerType::Uniform: + mpEmissiveSampler = EmissiveUniformSampler::create(pRenderContext, mpScene, mUniformSamplerOptions); + break; + default: + logError("Unknown emissive light sampler type"); + } + if (!mpEmissiveSampler) throw std::exception("Failed to create emissive light sampler"); + + recreateVars(); // Trigger recreation of the program vars. + } + + // Update the emissive sampler to the current frame. + assert(mpEmissiveSampler); + lightingChanged = mpEmissiveSampler->update(pRenderContext); + + // Disable emissive for the current frame if the sampler has no active lights. + if (mpEmissiveSampler->getLightCount() == 0) + { + mUseEmissiveLights = mUseEmissiveSampler = false; + } + } + } + + return lightingChanged; +} + +// Compute the maximum number of rays per pixel we'll trace. This depends on the current config and scene. +// This function should be called just before rendering, when everything has been updated. +uint32_t PathTracer::maxRaysPerPixel() const +{ + if (!mpScene) return 0; + + // Logic for determining what rays we need to trace. This should match what the shaders are doing. + bool traceShadowRays = mUseAnalyticLights || mUseEnvLight || mUseEmissiveSampler; + bool traceScatterRayFromLastPathVertex = + (mUseEnvLight && mSharedParams.useMIS) || + (mUseEmissiveLights && (!mUseEmissiveSampler || mSharedParams.useMIS)); + + uint32_t shadowRays = traceShadowRays ? mSharedParams.lightSamplesPerVertex * (mSharedParams.maxBounces + 1) : 0; + uint32_t scatterRays = mSharedParams.maxBounces + (traceScatterRayFromLastPathVertex ? 1 : 0); + uint32_t raysPerPath = shadowRays + scatterRays; + + return raysPerPath * mSharedParams.samplesPerPixel; +} + +bool PathTracer::beginFrame(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Update lights. Returns true if emissive lights have changed. + bool lightingChanged = updateLights(pRenderContext); + + mMaxRaysPerPixel = maxRaysPerPixel(); + + // Update refresh flag if changes that affect the output have occured. + Dictionary& dict = renderData.getDictionary(); + if (mOptionsChanged || lightingChanged) + { + auto flags = (Falcor::RenderPassRefreshFlags)(dict.keyExists(kRenderPassRefreshFlags) ? dict[Falcor::kRenderPassRefreshFlags] : 0u); + if (mOptionsChanged) flags |= Falcor::RenderPassRefreshFlags::RenderOptionsChanged; + if (lightingChanged) flags |= Falcor::RenderPassRefreshFlags::LightingChanged; + dict[Falcor::kRenderPassRefreshFlags] = (uint32_t)flags; + mOptionsChanged = false; + } + + // If we have no scene, just clear the outputs and return. + if (!mpScene) + { + for (auto it : kOutputChannels) + { + Texture* pDst = renderData[it.name]->asTexture().get(); + if (pDst) pRenderContext->clearTexture(pDst); + } + return false; + } + + // Configure depth-of-field. + const bool useDOF = mpScene->getCamera()->getApertureRadius() > 0.f; + if (useDOF && renderData[kViewDirInput] == nullptr) + { + logWarning("Depth-of-field requires the '" + std::string(kViewDirInput) + "' input. Expect incorrect shading."); + } + + // Get the PRNG start dimension from the dictionary as preceeding passes may have used some dimensions for lens sampling. + mSharedParams.prngDimension = dict.keyExists(kRenderPassPRNGDimension) ? dict[kRenderPassPRNGDimension] : 0u; + + return true; +} + +void PathTracer::endFrame(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Generate ray count output if it is exists. + Texture* pDstRayCount = renderData["rayCount"]->asTexture().get(); + if (pDstRayCount) + { + Texture* pSrcRayCount = mStatsLogger->getRayCountBuffer().get(); + if (pSrcRayCount == nullptr) + { + pRenderContext->clearUAV(pDstRayCount->getUAV().get(), uvec4(0, 0, 0, 0)); + } + else + { + assert(pDstRayCount && pSrcRayCount); + assert(pDstRayCount->getFormat() == pSrcRayCount->getFormat()); + assert(pDstRayCount->getWidth() == pSrcRayCount->getWidth() && pDstRayCount->getHeight() == pSrcRayCount->getHeight()); + pRenderContext->copyResource(pDstRayCount, pSrcRayCount); + } + } + + mSharedParams.frameCount++; +} + +void PathTracer::setStaticParams(ProgramBase* pProgram) const +{ + // Set compile-time constants on the given program. + // These defines should not modify the program vars. Do not trigger program vars re-creation. + // TODO: It's unnecessary to set these every frame. It should be done lazily, but the book-keeping is complicated. + Program::DefineList defines; + defines.add("SAMPLES_PER_PIXEL", std::to_string(mSharedParams.samplesPerPixel)); + defines.add("LIGHT_SAMPLES_PER_VERTEX", std::to_string(mSharedParams.lightSamplesPerVertex)); + defines.add("MAX_BOUNCES", std::to_string(mSharedParams.maxBounces)); + defines.add("FORCE_ALPHA_ONE", mSharedParams.forceAlphaOne ? "1" : "0"); + defines.add("USE_ANALYTIC_LIGHTS", mUseAnalyticLights ? "1" : "0"); + defines.add("USE_EMISSIVE_LIGHTS", mUseEmissiveLights ? "1" : "0"); + defines.add("USE_EMISSIVE_SAMPLER", mUseEmissiveSampler ? "1" : "0"); + defines.add("USE_ENV_LIGHT", mUseEnvLight ? "1" : "0"); + defines.add("USE_ENV_BACKGROUND", (mpEnvProbe && mSharedParams.useEnvBackground) ? "1" : "0"); + defines.add("USE_BRDF_SAMPLING", mSharedParams.useBRDFSampling ? "1" : "0"); + defines.add("USE_MIS", mSharedParams.useMIS ? "1" : "0"); + defines.add("MIS_HEURISTIC", std::to_string(mSharedParams.misHeuristic)); + defines.add("USE_RUSSIAN_ROULETTE", mSharedParams.useRussianRoulette ? "1" : "0"); + pProgram->addDefines(defines); +} diff --git a/Source/RenderPasses/PathTracer/PathTracer.h b/Source/RenderPasses/PathTracer/PathTracer.h new file mode 100644 index 000000000..5be33fd21 --- /dev/null +++ b/Source/RenderPasses/PathTracer/PathTracer.h @@ -0,0 +1,130 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" +#include "FalcorExperimental.h" +#include "Utils/Sampling/SampleGenerator.h" +#include "Utils/Debug/PixelDebug.h" +#include "Experimental/Scene/Lights/EnvProbe.h" +#include "Experimental/Scene/Lights/EmissiveUniformSampler.h" +#include "RenderGraph/RenderPassHelpers.h" +#include "PathTracerParams.h" +#include "Logging.h" + +using namespace Falcor; + +/** Base class for path tracers. +*/ +class PathTracer : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + +protected: + PathTracer() = default; + + virtual bool init(const Dictionary& dict); + virtual void recreateVars() {} + + void validateParameters(); + bool initLights(RenderContext* pRenderContext); + bool updateLights(RenderContext* pRenderContext); + uint32_t maxRaysPerPixel() const; + bool beginFrame(RenderContext* pRenderContext, const RenderData& renderData); + void endFrame(RenderContext* pRenderContext, const RenderData& renderData); + bool renderSamplingUI(Gui::Widgets& widget); + bool renderLightsUI(Gui::Widgets& widget); + void renderLoggingUI(Gui::Widgets& widget); + void setStaticParams(ProgramBase* pProgram) const; + + // Constants used in derived classes + static const std::string kViewDirInput; + static const std::string kAlbedoOutput; + static const ChannelList kInputChannels; + static const ChannelList kOutputChannels; + + // Internal state + Scene::SharedPtr mpScene; ///< Current scene. + + SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU sample generator. + EmissiveLightSampler::SharedPtr mpEmissiveSampler; ///< Emissive light sampler or nullptr if disabled. + EnvProbe::SharedPtr mpEnvProbe; ///< Environment map sampling (if used). + std::string mEnvProbeFilename; ///< Name of loaded environment map (stripped of full path). + + Logging::SharedPtr mStatsLogger; ///< Helper for collecting runtime tracing stats. + PixelDebug::SharedPtr mPixelDebugger; ///< Utility class for pixel debugging (print in shaders). + + // Configuration + PathTracerParams mSharedParams; ///< Host/device shared rendering parameters. + uint32_t mSelectedSampleGenerator = SAMPLE_GENERATOR_UNIFORM; ///< Which pseudorandom sample generator to use. + EmissiveLightSamplerType mSelectedEmissiveSampler = EmissiveLightSamplerType::Uniform; ///< Which emissive light sampler to use. + + EmissiveUniformSampler::EmissiveUniformSamplerOptions mUniformSamplerOptions; ///< Current options for the uniform sampler. + + // Runtime data + bool mOptionsChanged = false; ///< True if the config has changed since last frame. + bool mUseAnalyticLights = false; ///< True if analytic lights should be used for the current frame. See compile-time constant in StaticParams.slang. + bool mUseEnvLight = false; ///< True if env map light should be used for the current frame. See compile-time constant in StaticParams.slang. + bool mUseEmissiveLights = false; ///< True if emissive lights should be taken into account. See compile-time constant in StaticParams.slang. + bool mUseEmissiveSampler = false; ///< True if emissive light sampler should be used for the current frame. See compile-time constant in StaticParams.slang. + uint32_t mMaxRaysPerPixel = 0; ///< Maximum number of rays per pixel that will be traced. This is computed based on the current configuration. + + // Scripting +#define serialize(var) \ + if constexpr (!loadFromDict) dict[#var] = var; \ + else if (dict.keyExists(#var)) { if constexpr (std::is_same::value) var = (const std::string &)dict[#var]; else var = dict[#var]; vars.emplace(#var); } + + template + void serializePass(DictType& dict) + { + std::unordered_set vars; + + // Add variables here that should be serialized to/from the dictionary. + serialize(mSharedParams); + serialize(mSelectedSampleGenerator); + serialize(mSelectedEmissiveSampler); + serialize(mUniformSamplerOptions); + + if constexpr (loadFromDict) + { + for (const auto& v : dict) + { + if (vars.find(v.key()) == vars.end()) logWarning("Unknown field `" + v.key() + "` in a PathTracer dictionary"); + } + } + } +#undef serialize +}; diff --git a/Samples/Raytracing/PathTracer/PathTracer.vcxproj b/Source/RenderPasses/PathTracer/PathTracer.vcxproj similarity index 63% rename from Samples/Raytracing/PathTracer/PathTracer.vcxproj rename to Source/RenderPasses/PathTracer/PathTracer.vcxproj index dfcc6f9a0..9dd411902 100644 --- a/Samples/Raytracing/PathTracer/PathTracer.vcxproj +++ b/Source/RenderPasses/PathTracer/PathTracer.vcxproj @@ -1,4 +1,4 @@ - + @@ -10,67 +10,62 @@ x64 + + {99167900-C302-49E9-9C27-E6B3F666DB05} + Win32Proj + PathTracer + 10.0.17763.0 + PathTracer + + + + + + + + + + - - - + + - - - - + + + + + + + + + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} - - - Document - - - Document - - - - - - - - - - {0B41644B-B687-44D8-9CD5-9D41E0011561} - Win32Proj - PathTracer - 10.0.17763.0 - - - Application + DynamicLibrary true v141 Unicode + Data\RenderPasses\$(ProjectName) - Application + DynamicLibrary false v141 true Unicode + Data\RenderPasses\$(ProjectName) - - - - - - true @@ -84,8 +79,10 @@ Level3 Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - $(ProjectDir);%(AdditionalIncludeDirectories) + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows @@ -100,8 +97,10 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) - $(ProjectDir);%(AdditionalIncludeDirectories) + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows diff --git a/Source/RenderPasses/PathTracer/PathTracer.vcxproj.filters b/Source/RenderPasses/PathTracer/PathTracer.vcxproj.filters new file mode 100644 index 000000000..a27cbf993 --- /dev/null +++ b/Source/RenderPasses/PathTracer/PathTracer.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + + + Megakernel + + + + + + + Megakernel + + + + + + + Megakernel + + + Megakernel + + + Megakernel + + + + + + + + + {24ea07ed-cd8a-45d3-8fb2-f2db9984488b} + + + \ No newline at end of file diff --git a/Source/RenderPasses/PathTracer/PathTracerHelpers.slang b/Source/RenderPasses/PathTracer/PathTracerHelpers.slang new file mode 100644 index 000000000..107794d61 --- /dev/null +++ b/Source/RenderPasses/PathTracer/PathTracerHelpers.slang @@ -0,0 +1,418 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Utils/Math/MathConstants.slang" +#include "RenderPasses/PathTracer/PathTracerParams.h" + +// TODO: Which ones need __exported +import Scene; +import ShaderCommon; +__exported import Helpers; // For computeRayOrigin() +__exported import Utils.Math.MathHelpers; +__exported import Experimental.Scene.Lights.EnvProbe; +__exported import Experimental.Scene.Material.MaterialShading; +__exported import Experimental.Scene.Lights.EmissiveLightSampler; +__exported import Experimental.Scene.Lights.LightHelpers; +__exported import RenderPasses.PathTracer.PathData; + +static const float3 kDefaultBackgroundColor = float3(0, 0, 0); +static const float kRayTMax = FLT_MAX; + +// Logic for determining if we need to trace a scatter ray from the last path vertex. +// This is needed to account for direct illumination when NEE with MIS is enabled, or NEE is disabled. +// Note that these decisions are all static, no dynamic code is generated. +static const bool kTraceScatterRayFromLastPathVertex = + (kUseEnvLight && kUseMIS) || + (kUseEmissiveLights && (!kUseEmissiveSampler || kUseMIS)); + + +/** A light sample for any of the scene lights (analytic, envmap, and emissive). + The Li field is nonzero only if the sample is valid (no need to check the pdf). +*/ +struct SceneLightSample +{ + // Light sample + float3 dir; ///< Direction from the shading point to the light sample in world space (normalized). This is used for BRDF evaluation. + float distance; ///< Distance from the shading point to the light sample. + float3 Li; ///< Incident radiance at the shading point (unshadowed). Note: Already divided by the pdf and multiplied by MIS weight. + float pdf; ///< Probability density function with respect to solid angle (pdf == 0 for invalid samples). + + // Shadow ray parameters + float3 rayDir; ///< Ray direction for visibility evaluation (normalized). This may differ from 'dir' due to ray offsets. + float rayDistance; ///< Ray distance for visibility evaluation. This may differ from 'distance' due to ray offsets. +}; + +/** Evaluates the background in a particular direction. + This function should be called for screen-space samples that missed the scene. + The result should not be used as lighting, use evalDistantLight() instead. + \param[in] envProbe EnvProbe data. + \param[in] dir World-space direction (unnormalized). + \return Color (rgb). +*/ +float3 evalBackground(const EnvProbe envProbe, float3 dir) +{ + return kUseEnvBackground ? evalEnvProbe(envProbe, dir) : kDefaultBackgroundColor; +} + +/** Evaluates the currently configured heuristic for multiple importance sampling (MIS). + This version assumes one sample is taken from each of the sampling strategies. + \param[in] params PathTracer parameters. + \param[in] p0 Pdf for the first sampling strategy. + \param[in] p1 Pdf for the second sampling strategy. + \return Weight for the contribution from the first strategy (p0). +*/ +float evalMIS(const PathTracerParams params, float p0, float p1) +{ + switch (kMISHeuristic) + { + case MISHeuristic.BalanceHeuristic: + return p0 / (p0 + p1); + case MISHeuristic.PowerTwoHeuristic: + return p0 * p0 / (p0 * p0 + p1 * p1); + case MISHeuristic.PowerExpHeuristic: + float q0 = pow(p0, params.misPowerExponent); + float q1 = pow(p1, params.misPowerExponent); + return q0 / (q0 + q1); + default: + return 0.f; + } +} + +/** Evaluates the currently configured heuristic for multiple importance sampling (MIS). + \param[in] params PathTracer parameters. + \param[in] n0 Number of samples taken from the first sampling strategy. + \param[in] p0 Pdf for the first sampling strategy. + \param[in] n1 Number of samples taken from the second sampling strategy. + \param[in] p1 Pdf for the second sampling strategy. + \return Weight for the contribution from the first strategy (p0). +*/ +float evalMIS(const PathTracerParams params, float n0, float p0, float n1, float p1) +{ + switch (kMISHeuristic) + { + case MISHeuristic.BalanceHeuristic: + { + float q0 = n0 * p0; + float q1 = n1 * p1; + return q0 / (q0 + q1); + } + case MISHeuristic.PowerTwoHeuristic: + { + float q0 = (n0 * p0) * (n0 * p0); + float q1 = (n1 * p1) * (n1 * p1); + return q0 / (q0 + q1); + } + case MISHeuristic.PowerExpHeuristic: + { + float q0 = pow(n0 * p0, params.misPowerExponent); + float q1 = pow(n1 * p1, params.misPowerExponent); + return q0 / (q0 + q1); + } + default: + return 0.f; + } +} + +/** Returns the probability of selecting env map light sampling. +*/ +float getEnvLightSelectionPdf() +{ + float p0 = kUseEnvLight ? 1.f : 0.f; + float p1 = kUseAnalyticLights ? 1.f : 0.f; + float p2 = kUseEmissiveSampler ? 1.f : 0.f; + return p0 / (p0 + p1 + p2); +} + +/** Returns the probability of selecting emissive light sampling. +*/ +float getEmissiveLightSelectionPdf() +{ + float p0 = kUseEnvLight ? 1.f : 0.f; + float p1 = kUseAnalyticLights ? 1.f : 0.f; + float p2 = kUseEmissiveSampler ? 1.f : 0.f; + return p2 / (p0 + p1 + p2); +} + +/** Evaluates the probability density function for the BRDF sampling strategy used for the scatter ray. + \param[in] sd Describes the shading point. + \param[in] dir The normalized incident direction for which to evaluate the pdf. + \return Probability density with respect to solid angle from the shading point. +*/ +float evalPdfScatter(const ShadingData sd, const float3 dir) +{ + if (kUseBRDFSampling) + { + return evalPdfBRDF(sd, dir); + } + else // Cosine-weighted sampling + { + // Check the dot products. The sampling probability for back-facing directions is zero. + float NdotL = dot(sd.N, dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) return 0.f; + + return NdotL * M_1_PI; // pdf = cos(theta) / pi + } +} + +/** Samples a light source in the scene. + This function first stochastically selects a type of light source to sample, + and then calls that the sampling function for the chosen light type. + \param[in] params PathTracer parameters. + \param[in] envProbe EnvProbe data. + \param[in] emissiveSampler Emissive light sampler. + \param[in] sd Shading data. + \param[in] rayOrigin Ray origin for the shadow ray. + \param[in,out] sg SampleGenerator object. + \param[in] numSamples Total number of light samples that will be taken. + \return Generated light sample (if pdf=0.0 the sample is invalid, but it is sufficient and possibly more performance to test for Li!=(0,0,0)). +*/ +SceneLightSample sampleSceneLights(const PathTracerParams params, const EnvProbe envProbe, const EmissiveLightSampler emissiveSampler, const ShadingData sd, const float3 rayOrigin, inout SampleGenerator sg, const uint numSamples = 1) +{ + SceneLightSample ls = {}; + + // Set relative probabilities of the different sampling techniques. + // TODO: These should use estimated irradiance from each light type. Using equal probabilities for now. + // TODO: Refactor the selection pdf code the helpers above for evaluating the individual probabilities. + float p[3]; + p[0] = kUseEnvLight ? 1.f : 0.f; + p[1] = kUseAnalyticLights ? 1.f : 0.f; + p[2] = kUseEmissiveSampler ? 1.f : 0.f; + + // Normalize probabilities. Early out if zero. + float sum = p[0] + p[1] + p[2]; + if (sum == 0.f) return ls; + float invSum = 1.f / sum; + p[0] *= invSum; + p[1] *= invSum; + p[2] *= invSum; + + // Sample based on uniform random number. Rescale u to [0,1) afterwards. + float invPdf = 0.f; + float u = sampleNext1D(sg); + + // We use explicit checks for which light types are enabled so that the compiler + // can remove the unused code. It won't otherwise since u is unknown at compile time. + // Note: We're using valid flag to avoid conditional return statements that cause incorrect warnings when compiling for fxc (SM 5.1). + bool valid = false; + + if (kUseEnvLight) + { + if (u < p[0]) + { + float selectionPdf = p[0]; + + // Sample environment map. + EnvProbeSamplingResult lightSample; + sampleEnvProbe(envProbe, sampleNext2D(sg), lightSample); + + // Reject sample if lower hemisphere. + if (dot(sd.N, lightSample.wi) < kMinCosTheta) return ls; + + // Evaluate emitted radiance from the sampled direction. + // TODO: Move this into EnvProbe.slang + float3 Le = evalEnvProbe(envProbe, lightSample.wi); + + // Evaluate MIS with BRDF sampling as the other sampling strategy. + float pdf = selectionPdf * lightSample.pdf; + float misWeight = 1.f; + if (kUseMIS && pdf > 0.f) + { + float brdfPdf = evalPdfScatter(sd, lightSample.wi); + misWeight = evalMIS(params, numSamples, pdf, 1, brdfPdf); + } + + // Setup returned sample. + ls.rayDir = ls.dir = lightSample.wi; + ls.rayDistance = ls.distance = kRayTMax; + ls.pdf = pdf; + if (pdf > 0.f) ls.Li = Le * misWeight / (pdf * numSamples); + + valid = true; + } + u -= p[0]; + } + + if (kUseAnalyticLights) + { + if (u < p[1] && !valid) + { + // Sample analytic light source selected uniformly from the light list. + // TODO: Sample based on estimated contributions as pdf. + u /= p[1]; // Rescale to [0,1) + uint lightCount = gScene.getLightCount(); + uint lightIndex = min(uint(u * lightCount), lightCount - 1); + float selectionPdf = p[1] / lightCount; // TODO: Precompute 1.f/lightCount in cbuffer + + // Sample local light source. + AnalyticLightSample lightSample; + sampleLight(rayOrigin, gScene.getLight(lightIndex), sampleNext2D(sg), lightSample); + + // Reject sample if lower hemisphere. + if (dot(sd.N, lightSample.dir) < kMinCosTheta) return ls; + + // Setup returned sample. + // Analytic lights do not currently have a geometric representation in the scene. + // Do not worry about adjusting the ray to avoid self-intersections at the light. + ls.rayDir = ls.dir = lightSample.dir; + ls.rayDistance = ls.distance = lightSample.distance; + ls.pdf = selectionPdf * lightSample.pdf; + ls.Li = lightSample.Li / (selectionPdf * numSamples); + + valid = true; + } + u -= p[1]; + } + + if (kUseEmissiveSampler) + { + //if (u < p[2]) // Always true + if (!valid) + { + float selectionPdf = p[2]; + + // Sample emissive lights. + TriangleLightSample lightSample; + emissiveSampler.sampleLight(rayOrigin, sd.N, sg, lightSample); + + // Reject sample if lower hemisphere. + if (dot(sd.N, lightSample.dir) < kMinCosTheta) return ls; + + // Evaluate MIS with BRDF sampling as the other sampling strategy. + float pdf = selectionPdf * lightSample.pdf; + float misWeight = 1.f; + if (kUseMIS && pdf > 0.f) + { + float brdfPdf = evalPdfScatter(sd, lightSample.dir); + misWeight = evalMIS(params, numSamples, pdf, 1, brdfPdf); + } + + // Compute offset light sample position to reduce self-intersections at the light. + // We compute the shadow ray parameters based on the offset position. + float3 offsetPos = computeRayOrigin(lightSample.posW, lightSample.normalW); + float3 toLight = offsetPos - rayOrigin; + ls.rayDistance = length(toLight); + ls.rayDir = normalize(toLight); + + // Setup returned sample. + ls.dir = lightSample.dir; + ls.distance = lightSample.distance; + ls.pdf = pdf; + if (pdf > 0.f) ls.Li = lightSample.Le * misWeight / (pdf * numSamples); + + valid = true; + } + } + + return ls; +} + +/** Generates a shadow ray for sampling the light sources. + This should be called before generateScatterRay() as the latter updates the path throughput. + The function assumes path.origin is the ray origin for the shadow ray, and that the PathData + flags for the shadow ray have already been cleared. + \param[in] params Path tracer parameters. + \param[in] envProbe Environment map sampler. + \param[in] emissiveSampler Emissive light sampler. + \param[in] sd Shading data. + \param[in] i The sample index in the range [0, kLightSamplesPerVertex). + \param[in,out] pathData Path data. The path flags will be updated to enable the i:th shadow ray if a sample was generated. + \param[in,out] shadowRay Shadow ray parameters and unoccluded contribution for the generated sample. + \return True if a sample was generated, false otherwise. +*/ +bool generateShadowRay(const PathTracerParams params, const EnvProbe envProbe, const EmissiveLightSampler emissiveSampler, const ShadingData sd, const uint i, inout PathData path, inout ShadowRay shadowRay) +{ + // Sample the scene lights. + SceneLightSample ls = sampleSceneLights(params, envProbe, emissiveSampler, sd, path.origin, path.sg, kLightSamplesPerVertex); + + if (any(ls.Li > 0.f)) + { + float3 Lr = evalBRDFCosine(sd, ls.dir) * ls.Li * path.thp; + if (any(Lr > 0.f)) + { + // The sample is valid and has a non-zero contribution. + // Store ray parameters and unoccluded radiance, weighted by path throughput. + path.flags |= (uint(PathFlags::shadowRay) << i); + shadowRay.rayParams = float4(ls.rayDir, ls.rayDistance); + shadowRay.Lr = Lr; + return true; + } + } + return false; +} + +/** Generates a scatter ray or terminates the path. + The function uses BRDF sampling to generate ray parameters for the scatter ray. + The function assumes path.origin is the ray origin for the new ray. + \param[in] sd Shading data. + \param[in,out] pathData Path data. +*/ +void generateScatterRay(const ShadingData sd, inout PathData path) +{ + // Early out if we're at the last path vertex and the ray is not needed. + if (!kTraceScatterRayFromLastPathVertex && path.length == kMaxBounces) return; + + // Generate next path segment. + if (kUseBRDFSampling) + { + // Default path that uses BRDF importance sampling. + BRDFSample result; + sampleBRDF(sd, path.sg, result); + + path.dir = result.dir; + path.thp *= result.thp; + path.pdf = result.pdf; + } + else + { + // Fallback path that uses cosine-weighted hemisphere sampling. + float pdf; + float3 dir = sampleHemisphereCosine(sd, sampleNext2D(path.sg), pdf); // pdf = cos(theta) / pi + + // Check that L and V are in the positive hemisphere, mark the sample invalid otherwise. + // This is necessary for consistency with BRDF sampling. + float NdotL = dot(sd.N, dir); + if (min(sd.NdotV, NdotL) < kMinCosTheta) + { + path.thp = float3(0, 0, 0); // Just in case, evalBRDF() should already do this. + pdf = 0.f; + } + + path.dir = dir; + path.thp *= evalBRDF(sd, dir) * M_PI; // dot(N,L) / pdf = pi + path.pdf = pdf; + } + +// assert(!any(isnan(path.thp))); +// assert(!isnan(path.pdf)); + + // Pass on the shading normal. This is needed for MIS. + path.normal = sd.N; + + // Mark the ray as active only if the path throughput is nonzero. + // If we failed to generate a valid sample, the throughput will be zero. + if (any(path.thp > 0.f)) path.flags |= (uint)PathFlags::scatterRay; +} diff --git a/Source/RenderPasses/PathTracer/PathTracerParams.h b/Source/RenderPasses/PathTracer/PathTracerParams.h new file mode 100644 index 000000000..29eb0da65 --- /dev/null +++ b/Source/RenderPasses/PathTracer/PathTracerParams.h @@ -0,0 +1,107 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Data/HostDeviceSharedMacros.h" +#include "Experimental/Scene/Lights/EmissiveLightSamplerType.h" + +BEGIN_NAMESPACE_FALCOR + +// Define max supported path length (depends on #bits reserved for the counter). +// In practice, much smaller limits are typically used for perf reasons. +static const uint kMaxPathLengthBits = 8; +static const uint kMaxPathLength = (1 << kMaxPathLengthBits) - 1; + +static const uint kMaxLightSamplesPerVertex = 10; + +// Define ray indices. +static const uint32_t kRayTypeScatter = 0; +static const uint32_t kRayTypeShadow = 1; + +enum class MISHeuristic +{ + BalanceHeuristic = 0, ///< Balance heuristic. + PowerTwoHeuristic = 1, ///< Power heuristic (exponent = 2.0). + PowerExpHeuristic = 2, ///< Power heuristic (variable exponent). +}; + +/** Path tracer parameters. Shared between host and device. + + Note that if you add configuration parameters, do not forget to register + them with the scripting system in SCRIPT_BINDING() in PathTracer.cpp. +*/ +#ifdef HOST_CODE +struct PathTracerParams : Falcor::ScriptBindings::enable_to_string +#else +struct PathTracerParams +#endif +{ + // Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. + // Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx + // Note that the default initializers are ignored by Slang but used on the host. + + // General + uint samplesPerPixel = 1; ///< Number of samples (paths) per pixel. Use compile-time constant SAMPLES_PER_PIXEL in shader. + uint lightSamplesPerVertex = 1; ///< Number of light samples per path vertex. Use compile-time constant LIGHT_SAMPLES_PER_VERTEX in shader. + uint maxBounces = 3; ///< Max number of indirect bounces (0 = none), up to kMaxPathLength. Use compile-time constant MAX_BOUNCES in shader. + int forceAlphaOne = true; ///< Force the alpha channel to 1.0. Otherwise background will have alpha 0.0 and covered samples 1.0 to allow compositing. Use compile-time constant FORCE_ALPHA_ONE in shader. + + int clampDirect = false; ///< Clamp the radiance for direct illumination samples to 'thresholdDirect' to reduce fireflys. + int clampIndirect = false; ///< Clamp the radiance for indirect illumination samples to 'thresholdIndirect' to reduce fireflys. + float thresholdDirect = 10.f; + float thresholdIndirect = 10.f; + + // Lighting + int useAnalyticLights = true; ///< Use built-in analytic lights. + int useEmissiveLights = true; ///< Use emissive geometry as light sources. + int useEnvLight = true; ///< Use environment map as light source (if loaded). Use compile-time constant USE_ENV_LIGHT in shader. + int useEnvBackground = true; ///< Use environment map as background (if loaded). Use compile-time constant USE_ENV_BACKGROUND in shader. + + // Sampling + int useBRDFSampling = true; ///< Use BRDF importance sampling (otherwise cosine-weighted hemisphere sampling). Use compile-time constant USE_BRDF_SAMPLING in shader. + int useMIS = true; ///< Use multiple importance sampling (MIS). Use compile-time constant USE_MIS in shader. + uint misHeuristic = 1; /* (uint)MISHeuristic::PowerTwoHeuristic */ ///< MIS heuristic. Use compile-time constant MIS_HEURISTIC in shader + float misPowerExponent = 2.f; ///< MIS exponent for the power heuristic. This is only used when 'PowerExpHeuristic' is chosen. + + int useEmissiveLightSampling = true;///< Use emissive light importance sampling. + int useRussianRoulette = false; ///< Use Russian roulette. Use compile-time constant USE_RUSSIAN_ROULETTE in shader. + float probabilityAbsorption = 0.2f; ///< Probability of absorption for Russian roulette. + int useFixedSeed = false; ///< Use fixed random seed for the sample generator. This is useful for print() debugging. + + // Runtime data + uint2 frameDim = uint2(0, 0); ///< Current frame dimensions in pixels. + uint frameCount = 0; ///< Frame count since scene was loaded. + uint prngDimension = 0; ///< First available PRNG dimension. + + uint lightCountPoint = 0; ///< Number of point lights in the scene. + uint lightCountDirectional = 0; ///< Number of directional lights in the scene. + uint lightCountAnalyticArea = 0; ///< Number of analytic area lights in the scene. + uint _pad3; +}; + +END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/PathTracer/StaticParams.slang b/Source/RenderPasses/PathTracer/StaticParams.slang new file mode 100644 index 000000000..6fd69bd87 --- /dev/null +++ b/Source/RenderPasses/PathTracer/StaticParams.slang @@ -0,0 +1,63 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** Translation of defines set by the host to compile-time constants used to + configure the path tracer without overhead from dynamic control flow. + This will eventually be replaced by specialization constants in Slang. + + The host sets the following defines (booleans are 1=true, 0=false): + + SAMPLES_PER_PIXEL Number of paths to trace per pixel. + LIGHT_SAMPLES_PER_VERTEX Number of light sampler per path vertex. + MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). + USE_DEPTH_OF_FIELD Enables depth-of-field. + USE_ANALYTIC_LIGHTS Enables Falcor's analytic lights (point, directional). + USE_EMISSIVE_LIGHTS Enables use of emissive geometry as light sources. + USE_EMISSIVE_SAMPLER True if the emissive light sampler should be used. + USE_ENV_LIGHT True if env map is available and should be used as light source. + USE_ENV_BACKGROUND True if env map is available and should be used as background. + USE_BRDF_SAMPLING Enables BRDF importance sampling. + USE_MIS Enables multiple importance sampling. + USE_RUSSIAN_ROULETTE Enables Russian roulette for path termination. + MIS_HEURISTIC MIS heuristic enum value. +*/ + +static const uint kSamplesPerPixel = SAMPLES_PER_PIXEL; +static const uint kLightSamplesPerVertex = LIGHT_SAMPLES_PER_VERTEX; +static const uint kMaxBounces = MAX_BOUNCES; +static const bool kForceAlphaOne = FORCE_ALPHA_ONE; +static const bool kUseAnalyticLights = USE_ANALYTIC_LIGHTS; +static const bool kUseEmissiveLights = USE_EMISSIVE_LIGHTS; +static const bool kUseEmissiveSampler = USE_EMISSIVE_SAMPLER; +static const bool kUseEnvLight = USE_ENV_LIGHT; +static const bool kUseEnvBackground = USE_ENV_BACKGROUND; +static const bool kUseBRDFSampling = USE_BRDF_SAMPLING; +static const bool kUseMIS = USE_MIS; +static const bool kUseRussianRoulette = USE_RUSSIAN_ROULETTE; +static const uint kMISHeuristic = MIS_HEURISTIC; +//static const MISHeuristic kMISHeuristic = (MISHeuristic) MIS_HEURISTIC; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang b/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang new file mode 100644 index 000000000..2919d5918 --- /dev/null +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang @@ -0,0 +1,164 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +#include "PixelInspectorData.h" + +import Shading; +import Experimental.Scene.Material.MaterialHelpers; + +cbuffer PerFrameCB +{ + uint2 gResolution; + uint2 gSelectedPixel; + uint2 gWorldPositionCoord; + uint2 gWorldShadingNormalCoord; + uint2 gWorldBitangentCoord; + uint2 gWorldFaceNormalCoord; + uint2 gTextureCoordinateCoord; + uint2 gMaterialDiffuseOpacityCoord; + uint2 gMaterialSpecularRoughnessCoord; + uint2 gMaterialEmissiveCoord; + uint2 gMaterialExtraParamsCoord; + uint2 gLinearColorCoord; + uint2 gOutputColorCoord; + uint2 gVisBufferCoord; + + CameraData gCamera; +} + +Texture2D gWorldPosition; +Texture2D gWorldShadingNormal; +Texture2D gWorldBitangent; +Texture2D gWorldFaceNormal; +Texture2D gTextureCoordinate; +Texture2D gMaterialDiffuseOpacity; +Texture2D gMaterialSpecularRoughness; +Texture2D gMaterialEmissive; +Texture2D gMaterialExtraParams; +Texture2D gLinearColor; +Texture2D gOutputColor; +Texture2D gVisBuffer; + +RWStructuredBuffer gPixelDataBuffer; + +/** Load material parameters. + \return MaterialParams struct filled in with values that were available. +*/ +MaterialParams loadMaterialParams() +{ + MaterialParams matParams; + matParams = matParams.init(); + + matParams.diffuseOpacity = gMaterialDiffuseOpacity[gMaterialDiffuseOpacityCoord]; + matParams.specularRoughness = gMaterialSpecularRoughness[gMaterialSpecularRoughnessCoord]; + matParams.emissive = gMaterialEmissive[gMaterialEmissiveCoord]; + matParams.extraParams = gMaterialExtraParams[gMaterialExtraParamsCoord]; + + return matParams; +} + +/** Calculates the primary ray direction in world space. + The returned ray points from the camera origin towards the jittered pixel position. + The function assumes a point camera, no depth-of-field supported in this version. + \param[in] pixel Pixel coordinates with origin in the top-left corner. + \param[in] frameDim Frame dimensions in pixels. + \return World-space dir from camera origin to jittered pixel position (normalized). +*/ +float3 getCameraRayDir(uint2 pixel, uint2 frameDim, const CameraData camera) +{ + float2 p = (pixel + float2(0.5f, 0.5f)) / frameDim; // Pixel center on image plane in [0,1] where (0,0) is top-left + p += float2(-camera.jitterX, camera.jitterY); // Camera jitter offsets the sample by +-0.5 pixels. + float2 ndc = float2(2, -2) * p + float2(-1, 1); + float3 rayDir = ndc.x * camera.cameraU + ndc.y * camera.cameraV + camera.cameraW; + return normalize(rayDir); +} + +[numthreads(1, 1, 1)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + const uint2 pixelPos = gSelectedPixel; + + PixelData data; + data.init(); + + // Read material data + const float4 worldPos = gWorldPosition[gWorldPositionCoord]; + const float3 normal = gWorldShadingNormal[gWorldShadingNormalCoord].xyz; + const float3 bitangent = gWorldBitangent[gWorldBitangentCoord].xyz; + const float3 faceNormal = gWorldFaceNormal[gWorldFaceNormalCoord].xyz; + + // Compute the view vector. This must exactly match what the G-buffer pass is doing (jitter etc.). + // Note that we do not take depth-of-field into account as it would require exactly matching the + // sample generator between the passes, which is error prone. The host side will issue a warning instead. + const float3 viewDir = -getCameraRayDir(pixelPos, gResolution, gCamera); + + const GeometryParams geoParams = prepareGeometryParams(worldPos.xyz, viewDir, normal, bitangent, faceNormal, gTextureCoordinate[gTextureCoordinateCoord].xy); + const MaterialParams matParams = loadMaterialParams(); + const ShadingData sd = prepareShadingData(geoParams, matParams); + + data.posW = sd.posW; + data.texCoordU = sd.uv.x; + + data.normal = sd.N; + data.texCoordV = sd.uv.y; + + data.tangent = sd.T; + data.NdotV = sd.NdotV; + + data.bitangent = sd.B; + data.faceNormal = sd.faceN; + data.view = sd.V; + + data.diffuse = sd.diffuse; + data.opacity = sd.opacity; + + data.specular = sd.specular; + data.linearRoughness = sd.linearRoughness; + + data.emissive = sd.emissive; + data.ggxAlpha = sd.ggxAlpha; + + data.IoR = sd.IoR; + data.doubleSided = sd.doubleSided ? 1u : 0u; + + // Read output data + data.linearColor = gLinearColor[gLinearColorCoord]; + data.outputColor = gOutputColor[gOutputColorCoord]; + + // Compute luminance. Note we're using the Rec.709 color space now. + data.luminance = luminance(data.linearColor.rgb); + + // Visibility buffer + uint4 v = gVisBuffer[gVisBufferCoord]; + data.meshInstanceID = v.x; + data.triangleIndex = v.y; + data.barycentrics = asfloat(v.zw); + + // Store result + gPixelDataBuffer[0] = data; +} diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.h b/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.h new file mode 100644 index 000000000..9739afd3f --- /dev/null +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.h @@ -0,0 +1,132 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Data/HostDeviceSharedMacros.h" + +static const uint kInvalidIndex = 0xffffffff; + +/** Pixel data read out by the InspectorPass. +*/ +struct PixelData +{ + // Geometry data + float3 posW; + float texCoordU; + + float3 normal; + float texCoordV; + + float3 tangent; + float NdotV; + + float3 bitangent; + float _pad0; + + float3 faceNormal; + float _pad3; + + float3 view; + float _pad1; + + // Material data + float3 diffuse; + float opacity; + + float3 specular; + float linearRoughness; + + float3 emissive; + float ggxAlpha; + + float IoR; + uint doubleSided; + float luminance; + float _pad2; + + // Output data + float4 linearColor; + float4 outputColor; + + // Visibility data + uint meshInstanceID; + uint triangleIndex; + float2 barycentrics; + +#ifdef HOST_CODE + PixelData() + { + init(); + } +#endif + +#ifdef HOST_CODE + void init() +#else + [mutating] void init() +#endif + { + posW = float3(0, 0, 0); + texCoordU = 0; + + normal = float3(0, 0, 0); + texCoordV = 0; + + tangent = float3(0, 0, 0); + NdotV = 0; + + bitangent = float3(0, 0, 0); + _pad0 = 0; + + faceNormal = float3(0, 0, 0); + _pad3 = 0; + + view = float3(0, 0, 0); + _pad1 = 0; + + diffuse = float3(0, 0, 0); + opacity = 0; + + specular = float3(0, 0, 0); + linearRoughness = 0; + + emissive = float3(0, 0, 0); + ggxAlpha = 0; + + IoR = 0; + doubleSided = 0; + luminance = 0; + _pad2 = 0; + + linearColor = float4(0, 0, 0, 0); + outputColor = float4(0, 0, 0, 0); + + meshInstanceID = kInvalidIndex; + triangleIndex = kInvalidIndex; + barycentrics = float2(0, 0); + } +}; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp new file mode 100644 index 000000000..7ecdd9ab0 --- /dev/null +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp @@ -0,0 +1,352 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "PixelInspectorPass.h" +#include "PixelInspectorData.h" +#include "RenderGraph/RenderPassHelpers.h" + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("PixelInspectorPass", "Per pixel surface attributes inspector", PixelInspectorPass::create); +} + +namespace +{ + const char kShaderFile[] = "RenderPasses/PixelInspectorPass/PixelInspector.cs.slang"; + + const ChannelList kInputChannels = + { + { "posW", "gWorldPosition", "world space position", true /* optional */ }, + { "normW", "gWorldShadingNormal", "world space normal", true /* optional */ }, + { "bitangentW", "gWorldBitangent", "world space bitangent", true /* optional */ }, + { "faceNormalW", "gWorldFaceNormal", "face normal in world space", true /* optional */ }, + { "texC", "gTextureCoordinate", "texture coordinates", true /* optional */ }, + { "diffuseOpacity", "gMaterialDiffuseOpacity", "diffuse color and opacity", true /* optional */ }, + { "specRough", "gMaterialSpecularRoughness", "specular color and roughness", true /* optional */ }, + { "emissive", "gMaterialEmissive", "emissive color", true /* optional */ }, + { "matlExtra", "gMaterialExtraParams", "additional material data", true /* optional */ }, + { "linColor", "gLinearColor", "color pre tone-mapping", true /* optional */ }, + { "outColor", "gOutputColor", "color post tone-mapping", true /* optional */ }, + { "visBuffer", "gVisBuffer", "Visibility buffer", true /* optional */, ResourceFormat::RGBA32Uint }, + }; + const char kOutputChannel[] = "gPixelDataBuffer"; +} + +PixelInspectorPass::SharedPtr PixelInspectorPass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new PixelInspectorPass); + return pPass->init() ? pPass : nullptr; +} + +PixelInspectorPass::PixelInspectorPass() +{ + for (auto it : kInputChannels) + { + mAvailableInputs[it.name] = false; + } +} + +bool PixelInspectorPass::init() +{ + mpProgram = ComputeProgram::createFromFile(kShaderFile, "main", Program::DefineList(), Shader::CompilerFlags::TreatWarningsAsErrors); + if (!mpProgram) return false; + + mpVars = ComputeVars::create(mpProgram->getReflector()); + if (!mpVars) return false; + + mpState = ComputeState::create(); + if (!mpState) return false; + + mpState->setProgram(mpProgram); + + mpPixelDataBuffer = StructuredBuffer::create(mpProgram.get(), kOutputChannel); + if (!mpPixelDataBuffer) return false; + + return true; +} + +std::string PixelInspectorPass::getDesc() +{ + return + "Inspect geometric and material properties at a given pixel.\n" + "\n" + "Left-mouse click on a pixel to select it\n"; +} + +RenderPassReflection PixelInspectorPass::reflect(const CompileData& compileData) +{ + // Define the required resources here + RenderPassReflection reflector; + for (auto it : kInputChannels) + { + auto& f = reflector.addInput(it.name, it.desc).format(it.format); + if (it.optional) f.flags(RenderPassReflection::Field::Flags::Optional); + } + return reflector; +} + +void PixelInspectorPass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + for (auto it : kInputChannels) + { + mAvailableInputs[it.name] = renderData[it.name] != nullptr; + } + + if (!mpScene) return; + + // Set the camera + Camera::SharedPtr pCamera = mpScene->getCamera(); + ConstantBuffer::SharedPtr pCB = mpVars->getConstantBuffer("PerFrameCB"); + pCamera->setIntoConstantBuffer(pCB.get(), "gCamera"); + + if (pCamera->getApertureRadius() > 0.f) + { + // TODO: Take view dir as optional input. For now issue warning if DOF is enabled. + logWarning("Depth-of-field is enabled, but PixelInspectorPass assumes a pinhole camera. Expect the view vector to be inaccurate."); + } + + const float2 cursorPosition = mUseContinuousPicking ? mCursorPosition : mSelectedCursorPosition; + const uvec2 resolution = renderData.getDefaultTextureDims(); + mSelectedPixel = glm::min((uvec2)(cursorPosition * ((float2)resolution)), resolution - 1u); + + // Fill in the constant buffer. + mpVars["PerFrameCB"]["gResolution"] = resolution; + mpVars["PerFrameCB"]["gSelectedPixel"] = mSelectedPixel; + + // Bind all input buffers. + for (auto it : kInputChannels) + { + if (mAvailableInputs[it.name]) + { + Texture::SharedPtr pSrc = renderData[it.name]->asTexture(); + mpVars[it.texname] = pSrc; + + // If the texture has a different resolution, we need to scale the sampling coordinates accordingly. + const uvec2 srcResolution = uvec2(pSrc->getWidth(), pSrc->getHeight()); + const bool needsScaling = mScaleInputsToWindow && srcResolution != resolution; + const uvec2 scaledCoord = (uvec2)(((float2)(srcResolution * mSelectedPixel)) / ((float2)resolution)); + mpVars["PerFrameCB"][std::string(it.texname) + "Coord"] = needsScaling ? scaledCoord : mSelectedPixel; + + mIsInputInBounds[it.name] = glm::all(glm::lessThanEqual(mSelectedPixel, srcResolution)); + } + else + { + mpVars->setTexture(it.texname, nullptr); + } + } + + // Bind the output buffer. + mpVars[kOutputChannel] = mpPixelDataBuffer; + + // Run the inspector program. + pRenderContext->dispatch(mpState.get(), mpVars.get(), { 1u, 1u, 1u }); +} + +void PixelInspectorPass::renderUI(Gui::Widgets& widget) +{ + PixelData pixelData = *reinterpret_cast(mpPixelDataBuffer->map(Buffer::MapType::Read)); + mpPixelDataBuffer->unmap(); + + // Display the coordinates for the pixel at which information is retrieved. + widget.var("Looking at pixel", (int2&)mSelectedPixel, 0); + + widget.checkbox("Scale inputs to window size", mScaleInputsToWindow); + widget.checkbox("Continuously inspect pixels", mUseContinuousPicking); + widget.tooltip("If continuously inspecting pixels, you will always see the data for the pixel currently under your mouse.\n" + "Otherwise, left-mouse click on a pixel to select it.", true); + + const auto displayValues = [&pixelData, &widget, this](const std::vector& inputNames, const std::vector& values, const std::function& displayValues) + { + bool areAllInputsAvailable = true; + for (const std::string& inputName : inputNames) areAllInputsAvailable = areAllInputsAvailable && mAvailableInputs[inputName]; + + if (areAllInputsAvailable) + { + bool areAllInputsInBounds = true; + for (const std::string& inputName : inputNames) areAllInputsInBounds = areAllInputsInBounds && mIsInputInBounds[inputName]; + + if (areAllInputsInBounds) + { + displayValues(pixelData); + } + else + { + for (const std::string& value : values) + { + const std::string text = value + ": out of bounds"; + widget.text(text.c_str()); + } + } + return true; + } + return false; + }; + + // Display output data. + auto outputGroup = Gui::Group(widget, "Output data", true); + if (outputGroup.open()) + { + bool displayedData = displayValues({ "linColor" }, { "Linear color", "Luminance (cd/m2)" }, [&outputGroup](PixelData& pixelData) { + outputGroup.var("Linear color", pixelData.linearColor, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); + outputGroup.var("Luminance (cd/m2)", pixelData.luminance, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); + }); + + displayedData |= displayValues({ "outColor" }, { "Output color" }, [&outputGroup](PixelData& pixelData) { + outputGroup.var("Output color", pixelData.outputColor, 0.f, 1.f, 0.001f, false, "%.6f"); + }); + + if (displayedData == false) outputGroup.text("No input data"); + + outputGroup.release(); + } + + auto geometryGroup = Gui::Group(widget, "Geometry data", true); + if (geometryGroup.open()) + { + // Display geometry data + displayValues({ "posW" }, { "World position" }, [&geometryGroup](PixelData& pixelData) { + geometryGroup.var("World position", pixelData.posW, std::numeric_limits::lowest(), std::numeric_limits::max(), 0.001f, false, "%.6f"); + }); + + displayValues({ "normW" }, { "Shading normal" }, [&geometryGroup](PixelData& pixelData) { + geometryGroup.var("Shading normal", pixelData.normal, -1.f, 1.f, 0.001f, false, "%.6f"); + }); + + displayValues({ "normW", "bitangentW" }, { "Shading tangent" }, [&geometryGroup](PixelData& pixelData) { + geometryGroup.var("Shading tangent", pixelData.tangent, -1.f, 1.f, 0.001f, false, "%.6f"); + }); + + displayValues({ "bitangentW" }, { "Shading bitangent" }, [&geometryGroup](PixelData& pixelData) { + geometryGroup.var("Shading bitangent", pixelData.bitangent, -1.f, 1.f, 0.001f, false, "%.6f"); + }); + + displayValues({ "faceNormalW" }, { "Face normal" }, [&geometryGroup](PixelData& pixelData) { + geometryGroup.var("Face normal", pixelData.faceNormal, -1.f, 1.f, 0.001f, false, "%.6f"); + }); + + displayValues({ "texC" }, { "Texture coords" }, [&geometryGroup](PixelData& pixelData) { + float2 texCoords = float2(pixelData.texCoordU, pixelData.texCoordV); + geometryGroup.var("Texture coords", texCoords, std::numeric_limits::lowest(), std::numeric_limits::max(), 0.001f, false, "%.6f"); + }); + + geometryGroup.var("View vector", pixelData.view, -1.f, 1.f, 0.001f, false, "%.6f"); + + displayValues({ "normW" }, { "NdotV" }, [&geometryGroup](PixelData& pixelData) { + geometryGroup.var("NdotV", pixelData.NdotV, -1.f, 1.f, 0.001f, false, "%.6f"); + }); + + geometryGroup.release(); + } + + auto materialGroup = Gui::Group(widget, "Material data", true); + if (materialGroup.open()) + { + // Display material data + bool displayedData = displayValues({ "diffuseOpacity" }, { "Diffuse color", "Opacity" }, [&materialGroup](PixelData& pixelData) { + materialGroup.var("Diffuse color", pixelData.diffuse, 0.f, 1.f, 0.001f, false, "%.6f"); + materialGroup.var("Opacity", pixelData.opacity, 0.f, 1.f, 0.001f, false, "%.6f"); + }); + + displayedData |= displayValues({ "specRough" }, { "Specular color", "GGX Alpha", "Roughness" }, [&materialGroup](PixelData& pixelData) { + materialGroup.var("Specular color", pixelData.specular, 0.f, 1.f, 0.001f, false, "%.6f"); + materialGroup.var("GGX Alpha", pixelData.ggxAlpha, 0.f, 1.f, 0.001f, false, "%.6f"); + materialGroup.var("Roughness", pixelData.linearRoughness, 0.f, 1.f, 0.001f, false, "%.6f"); + }); + + displayedData |= displayValues({ "emissive" }, { "Emissive" }, [&materialGroup](PixelData& pixelData) { + materialGroup.var("Emissive", pixelData.emissive, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); + }); + + displayedData |= displayValues({ "matlExtra" }, { "IoR", "Double sided" }, [&materialGroup](PixelData& pixelData) { + materialGroup.var("IoR", pixelData.IoR, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); + materialGroup.checkbox("Double sided", (int&)pixelData.doubleSided); + }); + + if (displayedData == false) materialGroup.text("No input data"); + + materialGroup.release(); + } + + // Display visibility data + auto visGroup = Gui::Group(widget, "Visibility data", true); + if (visGroup.open()) + { + if (mAvailableInputs["visBuffer"]) + { + visGroup.var("MeshInstanceID", pixelData.meshInstanceID); + visGroup.var("TriangleIndex", pixelData.triangleIndex); + visGroup.var("Barycentrics", pixelData.barycentrics); + + if (pixelData.meshInstanceID != kInvalidIndex) + { + auto instanceData = mpScene->getMeshInstance(pixelData.meshInstanceID); + uint32_t matrixID = instanceData.globalMatrixID; + glm::mat4 M = mpScene->getAnimationController()->getGlobalMatrices()[matrixID]; + + visGroup.text("Transform:"); + visGroup.var("##col0", M[0]); + visGroup.var("##col1", M[1]); + visGroup.var("##col2", M[2]); + visGroup.var("##col3", M[3]); + + bool flipped = (instanceData.flags & MeshInstanceFlags::Flipped) != MeshInstanceFlags::None; + visGroup.checkbox("Flipped winding", flipped); + } + } + else + { + visGroup.text("No visibility data available"); + } + + visGroup.release(); + } +} + +void PixelInspectorPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) +{ + mpScene = pScene; +} + +bool PixelInspectorPass::onMouseEvent(const MouseEvent& mouseEvent) +{ + if (mouseEvent.type == MouseEvent::Type::Move) + { + mCursorPosition = mouseEvent.pos; + } + else if (mouseEvent.type == MouseEvent::Type::LeftButtonDown) + { + mSelectedCursorPosition = mouseEvent.pos; + } + + return false; +} diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h new file mode 100644 index 000000000..1b7df87f3 --- /dev/null +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h @@ -0,0 +1,72 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" + +using namespace Falcor; + +/** Pass extracting material information for the object under the cursor. +*/ +class PixelInspectorPass : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + + /** Create a new object + */ + static SharedPtr create(RenderContext* pRenderContext, const Dictionary& dict = {}); + + virtual std::string getDesc() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; + +private: + PixelInspectorPass(); + bool init(); + + // Internal state + Scene::SharedPtr mpScene; + ComputeProgram::SharedPtr mpProgram; + ComputeState::SharedPtr mpState; + ComputeVars::SharedPtr mpVars; + + StructuredBuffer::SharedPtr mpPixelDataBuffer; + + float2 mCursorPosition = float2(0.0f); + float2 mSelectedCursorPosition = float2(0.0f); + std::unordered_map mAvailableInputs; + std::unordered_map mIsInputInBounds; + + // UI variables + uvec2 mSelectedPixel = uvec2(0u); + bool mScaleInputsToWindow = false; + bool mUseContinuousPicking = false; +}; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj new file mode 100644 index 000000000..287bbc746 --- /dev/null +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj @@ -0,0 +1,106 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {80DDDE59-A412-4E64-880A-6B52697C967B} + Win32Proj + InspectorPass + 10.0.17763.0 + PixelInspectorPass + + + + + + + + + + + + + + + + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + + + + + + DynamicLibrary + true + v141 + Unicode + Data\RenderPasses\$(ProjectName) + + + DynamicLibrary + false + v141 + true + Unicode + Data\RenderPasses\$(ProjectName) + + + + + + + true + + + false + + + + + + Level3 + Disabled + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj.filters b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj.filters new file mode 100644 index 000000000..5261ba65a --- /dev/null +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang b/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang new file mode 100644 index 000000000..66abe541e --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang @@ -0,0 +1,143 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +__import Helpers; +__import ShaderCommon; +__import Shading; +__import Utils.Math.MathHelpers; + +#include "RenderPasses/SVGFPass/SVGFCommon.slang.h" + +cbuffer PerImageCB +{ + Texture2D gAlbedo; + Texture2D gHistoryLength; + Texture2D gIllumination; + Texture2D gLinearZAndNormal; + + int gStepSize; + float gPhiColor; + float gPhiNormal; +}; + +// computes a 3x3 gaussian blur of the variance, centered around +// the current pixel +float computeVarianceCenter(int2 ipos) +{ + float sum = 0.f; + + const float kernel[2][2] = { + { 1.0 / 4.0, 1.0 / 8.0 }, + { 1.0 / 8.0, 1.0 / 16.0 } + }; + + const int radius = 1; + for (int yy = -radius; yy <= radius; yy++) + { + for (int xx = -radius; xx <= radius; xx++) + { + const int2 p = ipos + int2(xx, yy); + const float k = kernel[abs(xx)][abs(yy)]; + sum += gIllumination.Load(int3(p, 0)).a * k; + } + } + + return sum; +} + +float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 +{ + const int2 ipos = int2(vsOut.posH.xy); + const int2 screenSize = getTextureDims(gAlbedo, 0); + + const float epsVariance = 1e-10; + const float kernelWeights[3] = { 1.0, 2.0 / 3.0, 1.0 / 6.0 }; + + // constant samplers to prevent the compiler from generating code which + // fetches the sampler descriptor from memory for each texture access + const float4 illuminationCenter = gIllumination.Load(int3(ipos, 0)); + const float lIlluminationCenter = luminance(illuminationCenter.rgb); + + // variance, filtered using 3x3 gaussin blur + const float var = computeVarianceCenter(ipos); + + // number of temporally integrated pixels + const float historyLength = gHistoryLength[ipos].x; + + const float2 zCenter = gLinearZAndNormal[ipos].xy; + if (zCenter.x < 0) + { + // not a valid depth => must be envmap => do not filter + return illuminationCenter; + } + const float3 nCenter = oct_to_ndir_snorm(gLinearZAndNormal[ipos].zw); + + const float phiLIllumination = gPhiColor * sqrt(max(0.0, epsVariance + var.r)); + const float phiDepth = max(zCenter.y, 1e-8) * gStepSize; + + // explicitly store/accumulate center pixel with weight 1 to prevent issues + // with the edge-stopping functions + float sumWIllumination = 1.0; + float4 sumIllumination = illuminationCenter; + + for (int yy = -2; yy <= 2; yy++) + { + for (int xx = -2; xx <= 2; xx++) + { + const int2 p = ipos + int2(xx, yy) * gStepSize; + const bool inside = all(p >= int2(0,0)) && all(p < screenSize); + + const float kernel = kernelWeights[abs(xx)] * kernelWeights[abs(yy)]; + + if (inside && (xx != 0 || yy != 0)) // skip center pixel, it is already accumulated + { + const float4 illuminationP = gIllumination.Load(int3(p, 0)); + const float lIlluminationP = luminance(illuminationP.rgb); + const float zP = gLinearZAndNormal[p].x; + const float3 nP = oct_to_ndir_snorm(gLinearZAndNormal[p].zw); + + // compute the edge-stopping functions + const float2 w = computeWeight( + zCenter.x, zP, phiDepth * length(float2(xx, yy)), + nCenter, nP, gPhiNormal, + lIlluminationCenter, lIlluminationP, phiLIllumination); + + const float wIllumination = w.x * kernel; + + // alpha channel contains the variance, therefore the weights need to be squared, see paper for the formula + sumWIllumination += wIllumination; + sumIllumination += float4(wIllumination.xxx, wIllumination * wIllumination) * illuminationP; + } + } + } + + // renormalization is different for variance, check paper for the formula + float4 filteredIllumination = float4(sumIllumination / float4(sumWIllumination.xxx, sumWIllumination * sumWIllumination)); + + return filteredIllumination; +} diff --git a/Source/RenderPasses/SVGFPass/SVGFCommon.slang.h b/Source/RenderPasses/SVGFPass/SVGFCommon.slang.h new file mode 100644 index 000000000..8ceef4712 --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFCommon.slang.h @@ -0,0 +1,64 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +int2 getTextureDims(Texture2D tex, uint mip) +{ + uint w, h; + tex.GetDimensions(w, h); + return int2(w,h); +} + +float computeWeight( + float depthCenter, float depthP, float phiDepth, + float3 normalCenter, float3 normalP, float phiNormal, + float luminanceIllumCenter, float luminanceIllumP, float phiIllum) +{ + const float weightNormal = pow(saturate(dot(normalCenter, normalP)), phiNormal); + const float weightZ = (phiDepth == 0) ? 0.0f : abs(depthCenter - depthP) / phiDepth; + const float weightLillum = abs(luminanceIllumCenter - luminanceIllumP) / phiIllum; + + const float weightIllum = exp(0.0 - max(weightLillum, 0.0) - max(weightZ, 0.0)) * weightNormal; + + return weightIllum; +} + +struct FullScreenPassVsOut +{ + float2 texC : TEXCOORD; +#ifndef _VIEWPORT_MASK + float4 posH : SV_POSITION; +#else + float4 posH : POSITION; +#endif +#ifdef _SINGLE_PASS_STEREO + float4 rightPosH : NV_X_RIGHT; + uint4 viewportMask : NV_VIEWPORT_MASK; + uint renderTargetIndex : SV_RenderTargetArrayIndex; +#endif +}; diff --git a/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang b/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang new file mode 100644 index 000000000..8b6385789 --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang @@ -0,0 +1,126 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +__import Helpers; +__import ShaderCommon; +__import Shading; +__import Utils.Math.MathHelpers; + +#include "RenderPasses/SVGFPass/SVGFCommon.slang.h" + +cbuffer PerImageCB +{ + Texture2D gIllumination; + Texture2D gMoments; + Texture2D gHistoryLength; + Texture2D gLinearZAndNormal; + + float gPhiColor; + float gPhiNormal; +}; + +float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 +{ + float4 posH = vsOut.posH; + int2 ipos = int2(posH.xy); + + float h = gHistoryLength[ipos].x; + int2 screenSize = getTextureDims(gHistoryLength, 0); + + if (h < 4.0) // not enough temporal history available + { + float sumWIllumination = 0.0; + float3 sumIllumination = float3(0.0, 0.0, 0.0); + float2 sumMoments = float2(0.0, 0.0); + + const float4 illuminationCenter = gIllumination[ipos]; + const float lIlluminationCenter = luminance(illuminationCenter.rgb); + + const float2 zCenter = gLinearZAndNormal[ipos].xy; + if (zCenter.x < 0) + { + // current pixel does not a valid depth => must be envmap => do nothing + return illuminationCenter; + } + const float3 nCenter = oct_to_ndir_snorm(gLinearZAndNormal[ipos].zw); + const float phiLIllumination = gPhiColor; + const float phiDepth = max(zCenter.y, 1e-8) * 3.0; + + // compute first and second moment spatially. This code also applies cross-bilateral + // filtering on the input illumination. + const int radius = 3; + + for (int yy = -radius; yy <= radius; yy++) + { + for (int xx = -radius; xx <= radius; xx++) + { + const int2 p = ipos + int2(xx, yy); + const bool inside = all(p >= int2(0,0)) && all(p < screenSize); + const bool samePixel = (xx ==0 && yy == 0); + const float kernel = 1.0; + + if (inside) + { + const float3 illuminationP = gIllumination[p].rgb; + const float2 momentsP = gMoments[p].xy; + const float lIlluminationP = luminance(illuminationP.rgb); + const float zP = gLinearZAndNormal[p].x; + const float3 nP = oct_to_ndir_snorm(gLinearZAndNormal[p].zw); + + const float w = computeWeight( + zCenter.x, zP, phiDepth * length(float2(xx, yy)), + nCenter, nP, gPhiNormal, + lIlluminationCenter, lIlluminationP, phiLIllumination); + + sumWIllumination += w; + sumIllumination += illuminationP * w; + sumMoments += momentsP * w; + } + } + } + + // Clamp sum to >0 to avoid NaNs. + sumWIllumination = max(sumWIllumination, 1e-6f); + + sumIllumination /= sumWIllumination; + sumMoments /= sumWIllumination; + + // compute variance using the first and second moments + float variance = sumMoments.g - sumMoments.r * sumMoments.r; + + // give the variance a boost for the first frames + variance *= 4.0 / h; + + return float4(sumIllumination, variance.r); + } + else + { + // do nothing, pass data unmodified + return gIllumination[ipos]; + } +} diff --git a/Samples/Effects/AmbientOcclusion/Data/AOPrePass.ps.hlsl b/Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang similarity index 80% rename from Samples/Effects/AmbientOcclusion/Data/AOPrePass.ps.hlsl rename to Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang index 15d916f14..3a2f41708 100644 --- a/Samples/Effects/AmbientOcclusion/Data/AOPrePass.ps.hlsl +++ b/Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,22 +25,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ + +__import Helpers; __import ShaderCommon; __import Shading; -__import DefaultVS; -struct PSOut +#include "RenderPasses/SVGFPass/SVGFCommon.slang.h" + +cbuffer PerImageCB { - float4 color : SV_TARGET0; - float4 normal: SV_TARGET1; + Texture2D gAlbedo; + Texture2D gEmission; + Texture2D gIllumination; }; -PSOut main(VertexOut vsOut) +float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 { - PSOut psOut; - - psOut.color = 1.0.rrrr; - psOut.normal = float4(vsOut.normalW * 0.5f + 0.5f, 1.0f); + const int2 ipos = int2(vsOut.posH.xy); - return psOut; + return gAlbedo[ipos] * gIllumination[ipos] + gEmission[ipos]; } diff --git a/Samples/Core/SimpleDeferred/Data/DeferredPass.ps.hlsl b/Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang similarity index 77% rename from Samples/Core/SimpleDeferred/Data/DeferredPass.ps.hlsl rename to Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang index 116e219fd..a2b5989ea 100644 --- a/Samples/Core/SimpleDeferred/Data/DeferredPass.ps.hlsl +++ b/Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,27 +25,26 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import DefaultVS; -__import ShaderCommon; +__import Helpers; +__import ShaderCommon; __import Shading; +__import Utils.Math.MathHelpers; + +#include "RenderPasses/SVGFPass/SVGFCommon.slang.h" -struct PsOut +cbuffer PerImageCB { - float4 fragColor0 : SV_TARGET0; - float4 fragColor1 : SV_TARGET1; - float4 fragColor2 : SV_TARGET2; + Texture2D gLinearZ; + Texture2D gNormal; }; - -PsOut main(VertexOut vOut) +float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 { - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); - - PsOut psOut; - psOut.fragColor0 = float4(sd.posW, 1); - psOut.fragColor1 = float4(sd.N, sd.linearRoughness); - psOut.fragColor2 = float4(sd.diffuse, sd.opacity); + float4 fragCoord = vsOut.posH; + const int2 ipos = int2(fragCoord.xy); - return psOut; + const float2 nPacked = ndir_to_oct_snorm(gNormal[ipos].xyz); + return float4(gLinearZ[ipos].xy, nPacked.x, nPacked.y); } + diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.cpp b/Source/RenderPasses/SVGFPass/SVGFPass.cpp new file mode 100644 index 000000000..efef4b11e --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFPass.cpp @@ -0,0 +1,426 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "SVGFPass.h" + +/* +TODO: +- clean up shaders +- clean up UI: tooltips, etc. +- handle skybox pixels +- enum for fbo channel indices +*/ + +namespace +{ + // Shader source files + const char kPackLinearZAndNormalShader[] = "RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang"; + const char kReprojectShader[] = "RenderPasses/SVGFPass/SVGFReproject.ps.slang"; + const char kAtrousShader[] = "RenderPasses/SVGFPass/SVGFAtrous.ps.slang"; + const char kFilterMomentShader[] = "RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang"; + const char kFinalModulateShader[] = "RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang"; + + // Names of valid entries in the parameter dictionary. + const char kEnabled[] = "Enabled"; + const char kIterations[] = "Iterations"; + const char kFeedbackTap[] = "FeedbackTap"; + const char kVarianceEpsilon[] = "VarianceEpsilon"; + const char kPhiColor[] = "PhiColor"; + const char kPhiNormal[] = "PhiNormal"; + const char kAlpha[] = "Alpha"; + const char kMomentsAlpha[] = "MomentsAlpha"; + + // Input buffer names + const char kInputBufferAlbedo[] = "Albedo"; + const char kInputBufferColor[] = "Color"; + const char kInputBufferEmission[] = "Emission"; + const char kInputBufferWorldPosition[] = "WorldPosition"; + const char kInputBufferWorldNormal[] = "WorldNormal"; + const char kInputBufferPosNormalFwidth[] = "PositionNormalFwidth"; + const char kInputBufferLinearZ[] = "LinearZ"; + const char kInputBufferMotionVector[] = "MotionVec"; + + // Internal buffer names + const char kInternalBufferPreviousLinearZAndNormal[] = "Previous Linear Z and Packed Normal"; + const char kInternalBufferPreviousLighting[] = "Previous Lighting"; + const char kInternalBufferPreviousMoments[] = "Previous Moments"; + + // Output buffer name + const char kOutputBufferFilteredImage[] = "Filtered image"; +} + +// Don't remove this. it's required for hot-reload to function properly +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +extern "C" __declspec(dllexport) void getPasses(Falcor::RenderPassLibrary& lib) +{ + lib.registerClass("SVGFPass", "SVGF Denoising Pass", SVGFPass::create); +} + +SVGFPass::SharedPtr SVGFPass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new SVGFPass); + return pPass->init(dict) ? pPass : nullptr; +} + +bool SVGFPass::init(const Dictionary& dict) +{ + for (const auto& v : dict) + { + if (v.key() == kEnabled) mFilterEnabled = v.val(); + else if (v.key() == kIterations) mFilterIterations = v.val(); + else if (v.key() == kFeedbackTap) mFeedbackTap = v.val(); + else if (v.key() == kVarianceEpsilon) mVarainceEpsilon = v.val(); + else if (v.key() == kPhiColor) mPhiColor = v.val(); + else if (v.key() == kPhiNormal) mPhiNormal = v.val(); + else if (v.key() == kAlpha) mAlpha = v.val(); + else if (v.key() == kMomentsAlpha) mMomentsAlpha = v.val(); + else + { + logWarning("Unknown field `" + v.key() + "` in SVGFPass dictionary"); + } + } + + if (!(mpPackLinearZAndNormal = FullScreenPass::create(kPackLinearZAndNormalShader))) + { + logWarning(std::string("Error creating ") + kPackLinearZAndNormalShader + " shader."); + } + if (!(mpReprojection = FullScreenPass::create(kReprojectShader))) + { + logWarning(std::string("Error creating ") + kReprojectShader + " shader."); + } + if (!(mpAtrous = FullScreenPass::create(kAtrousShader))) + { + logWarning(std::string("Error creating ") + kAtrousShader + " shader."); + } + if (!(mpFilterMoments = FullScreenPass::create(kFilterMomentShader))) + { + logWarning(std::string("Error creating ") + kFilterMomentShader + " shader."); + } + if (!(mpFinalModulate = FullScreenPass::create(kFinalModulateShader))) + { + logWarning(std::string("Error creating ") + kFinalModulateShader + " shader."); + } + + return (mpPackLinearZAndNormal && mpReprojection && mpAtrous && mpFilterMoments && mpFinalModulate); +} + +Dictionary SVGFPass::getScriptingDictionary() +{ + Dictionary dict; + dict[kEnabled] = mFilterEnabled; + dict[kIterations] = mFilterIterations; + dict[kFeedbackTap] = mFeedbackTap; + dict[kVarianceEpsilon] = mVarainceEpsilon; + dict[kPhiColor] = mPhiColor; + dict[kPhiNormal] = mPhiNormal; + dict[kAlpha] = mAlpha; + dict[kMomentsAlpha] = mMomentsAlpha; + return dict; +} + +/* +Reproject: + - takes: motion, color, prevLighting, prevMoments, linearZ, prevLinearZ, historyLen + returns: illumination, moments, historyLength +Variance/filter moments: + - takes: illumination, moments, history length, normal+depth + - returns: filtered illumination+variance (to ping pong fbo) +a-trous: + - takes: albedo, filtered illumination+variance, normal+depth, history length + - returns: final color +*/ + +RenderPassReflection SVGFPass::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + + reflector.addInput(kInputBufferAlbedo, "Albedo"); + reflector.addInput(kInputBufferColor, "Color"); + reflector.addInput(kInputBufferEmission, "Emission"); + reflector.addInput(kInputBufferWorldPosition, "World Position"); + reflector.addInput(kInputBufferWorldNormal, "World Normal"); + reflector.addInput(kInputBufferPosNormalFwidth, "PositionNormalFwidth"); + reflector.addInput(kInputBufferLinearZ, "LinearZ"); + reflector.addInput(kInputBufferMotionVector, "Motion vectors"); + + reflector.addInternal(kInternalBufferPreviousLinearZAndNormal, "Previous Linear Z and Packed Normal") + .format(ResourceFormat::RGBA32Float) + .bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource) + ; + reflector.addInternal(kInternalBufferPreviousLighting, "Previous Filtered Lighting") + .format(ResourceFormat::RGBA32Float) + .bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource) + ; + reflector.addInternal(kInternalBufferPreviousMoments, "Previous Moments") + .format(ResourceFormat::RG32Float) + .bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource) + ; + + reflector.addOutput(kOutputBufferFilteredImage, "Filtered image").format(ResourceFormat::RGBA16Float); + + return reflector; +} + +void SVGFPass::compile(RenderContext* pRenderContext, const CompileData& compileData) +{ + allocateFbos(compileData.defaultTexDims, pRenderContext); + mBuffersNeedClear = true; +} + +void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + Texture::SharedPtr pAlbedoTexture = renderData[kInputBufferAlbedo]->asTexture(); + Texture::SharedPtr pColorTexture = renderData[kInputBufferColor]->asTexture(); + Texture::SharedPtr pEmissionTexture = renderData[kInputBufferEmission]->asTexture(); + Texture::SharedPtr pWorldPositionTexture = renderData[kInputBufferWorldPosition]->asTexture(); + Texture::SharedPtr pWorldNormalTexture = renderData[kInputBufferWorldNormal]->asTexture(); + Texture::SharedPtr pPosNormalFwidthTexture = renderData[kInputBufferPosNormalFwidth]->asTexture(); + Texture::SharedPtr pLinearZTexture = renderData[kInputBufferLinearZ]->asTexture(); + Texture::SharedPtr pMotionVectorTexture = renderData[kInputBufferMotionVector]->asTexture(); + + Texture::SharedPtr pOutputTexture = renderData[kOutputBufferFilteredImage]->asTexture(); + + assert(mpFilteredIlluminationFbo && + mpFilteredIlluminationFbo->getWidth() == pAlbedoTexture->getWidth() && + mpFilteredIlluminationFbo->getHeight() == pAlbedoTexture->getHeight()); + + if (mBuffersNeedClear) + { + clearBuffers(pRenderContext, renderData); + mBuffersNeedClear = false; + } + + if (mFilterEnabled) + { + // Grab linear z and its derivative and also pack the normal into + // the last two channels of the mpLinearZAndNormalFbo. + computeLinearZAndNormal(pRenderContext, pLinearZTexture, pWorldNormalTexture); + + // Demodulate input color & albedo to get illumination and lerp in + // reprojected filtered illumination from the previous frame. + // Stores the result as well as initial moments and an updated + // per-pixel history length in mpCurReprojFbo. + Texture::SharedPtr pPrevLinearZAndNormalTexture = + renderData[kInternalBufferPreviousLinearZAndNormal]->asTexture(); + computeReprojection(pRenderContext, pAlbedoTexture, pColorTexture, pEmissionTexture, + pMotionVectorTexture, pPosNormalFwidthTexture, + pPrevLinearZAndNormalTexture); + + // Do a first cross-bilateral filtering of the illumination and + // estimate its variance, storing the result into a float4 in + // mpPingPongFbo[0]. Takes mpCurReprojFbo as input. + computeFilteredMoments(pRenderContext); + + // Filter illumination from mpCurReprojFbo[0], storing the result + // in mpPingPongFbo[0]. Along the way (or at the end, depending on + // the value of mFeedbackTap), save the filtered illumination for + // next time into mpFilteredPastFbo. + computeAtrousDecomposition(pRenderContext, pAlbedoTexture); + + // Compute albedo * filtered illumination and add emission back in. + mpFinalModulate["gAlbedo"] = pAlbedoTexture; + mpFinalModulate["gEmission"] = pEmissionTexture; + mpFinalModulate["gIllumination"] = mpPingPongFbo[0]->getColorTexture(0); + mpFinalModulate->execute(pRenderContext, mpFinalFbo); + + // Blit into the output texture. + pRenderContext->blit(mpFinalFbo->getColorTexture(0)->getSRV(), pOutputTexture->getRTV()); + + // Swap resources so we're ready for next frame. + std::swap(mpCurReprojFbo, mpPrevReprojFbo); + pRenderContext->blit(mpLinearZAndNormalFbo->getColorTexture(0)->getSRV(), + pPrevLinearZAndNormalTexture->getRTV()); + } + else + { + pRenderContext->blit(pColorTexture->getSRV(), pOutputTexture->getRTV()); + } +} + +void SVGFPass::allocateFbos(uint2 dim, RenderContext* pRenderContext) +{ + { + // Screen-size FBOs with 3 MRTs: one that is RGBA32F, one that is + // RG32F for the luminance moments, and one that is R16F. + Fbo::Desc desc; + desc.setSampleCount(0); + desc.setColorTarget(0, Falcor::ResourceFormat::RGBA32Float); // illumination + desc.setColorTarget(1, Falcor::ResourceFormat::RG32Float); // moments + desc.setColorTarget(2, Falcor::ResourceFormat::R16Float); // history length + mpCurReprojFbo = Fbo::create2D(dim.x, dim.y, desc); + mpPrevReprojFbo = Fbo::create2D(dim.x, dim.y, desc); + } + + { + // Screen-size RGBA32F buffer for linear Z, derivative, and packed normal + Fbo::Desc desc; + desc.setColorTarget(0, Falcor::ResourceFormat::RGBA32Float); + mpLinearZAndNormalFbo = Fbo::create2D(dim.x, dim.y, desc); + } + + { + // Screen-size FBOs with 1 RGBA32F buffer + Fbo::Desc desc; + desc.setColorTarget(0, Falcor::ResourceFormat::RGBA32Float); + mpPingPongFbo[0] = Fbo::create2D(dim.x, dim.y, desc); + mpPingPongFbo[1] = Fbo::create2D(dim.x, dim.y, desc); + mpFilteredPastFbo = Fbo::create2D(dim.x, dim.y, desc); + mpFilteredIlluminationFbo = Fbo::create2D(dim.x, dim.y, desc); + mpFinalFbo = Fbo::create2D(dim.x, dim.y, desc); + } + + mBuffersNeedClear = true; +} + +void SVGFPass::clearBuffers(RenderContext* pRenderContext, const RenderData& renderData) +{ + pRenderContext->clearFbo(mpPingPongFbo[0].get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + pRenderContext->clearFbo(mpPingPongFbo[1].get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + pRenderContext->clearFbo(mpLinearZAndNormalFbo.get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + pRenderContext->clearFbo(mpFilteredPastFbo.get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + pRenderContext->clearFbo(mpCurReprojFbo.get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + pRenderContext->clearFbo(mpPrevReprojFbo.get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + pRenderContext->clearFbo(mpFilteredIlluminationFbo.get(), glm::vec4(0), 1.0f, 0, FboAttachmentType::All); + + pRenderContext->clearTexture(renderData[kInternalBufferPreviousLinearZAndNormal]->asTexture().get()); + pRenderContext->clearTexture(renderData[kInternalBufferPreviousLighting]->asTexture().get()); + pRenderContext->clearTexture(renderData[kInternalBufferPreviousMoments]->asTexture().get()); +} + +// Extracts linear z and its derivative from the linear Z texture and packs +// the normal from the world normal texture and packes them into the FBO. +// (It's slightly wasteful to copy linear z here, but having this all +// together in a single buffer is a small simplification, since we make a +// copy of it to refer to in the next frame.) +void SVGFPass::computeLinearZAndNormal(RenderContext* pRenderContext, Texture::SharedPtr pLinearZTexture, + Texture::SharedPtr pWorldNormalTexture) +{ + mpPackLinearZAndNormal["gLinearZ"] = pLinearZTexture; + mpPackLinearZAndNormal["gNormal"] = pWorldNormalTexture; + + mpPackLinearZAndNormal->execute(pRenderContext, mpLinearZAndNormalFbo); +} + +void SVGFPass::computeReprojection(RenderContext* pRenderContext, Texture::SharedPtr pAlbedoTexture, + Texture::SharedPtr pColorTexture, Texture::SharedPtr pEmissionTexture, + Texture::SharedPtr pMotionVectorTexture, + Texture::SharedPtr pPositionNormalFwidthTexture, + Texture::SharedPtr pPrevLinearZTexture) +{ + // Setup textures for our reprojection shader pass + mpReprojection["gMotion"] = pMotionVectorTexture; + mpReprojection["gColor"] = pColorTexture; + mpReprojection["gEmission"] = pEmissionTexture; + mpReprojection["gAlbedo"] = pAlbedoTexture; + mpReprojection["gPositionNormalFwidth"] = pPositionNormalFwidthTexture; + mpReprojection["gPrevIllum"] = mpFilteredPastFbo->getColorTexture(0); + mpReprojection["gPrevMoments"] = mpPrevReprojFbo->getColorTexture(1); + mpReprojection["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); + mpReprojection["gPrevLinearZAndNormal"] = pPrevLinearZTexture; + mpReprojection["gPrevHistoryLength"] = mpPrevReprojFbo->getColorTexture(2); + + // Setup variables for our reprojection pass + mpReprojection["PerImageCB"]["gAlpha"] = mAlpha; + mpReprojection["PerImageCB"]["gMomentsAlpha"] = mMomentsAlpha; + + mpReprojection->execute(pRenderContext, mpCurReprojFbo); +} + +void SVGFPass::computeFilteredMoments(RenderContext* pRenderContext) +{ + mpFilterMoments["gIllumination"] = mpCurReprojFbo->getColorTexture(0); + mpFilterMoments["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); + mpFilterMoments["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); + mpFilterMoments["gMoments"] = mpCurReprojFbo->getColorTexture(1); + + mpFilterMoments["PerImageCB"]["gPhiColor"] = mPhiColor; + mpFilterMoments["PerImageCB"]["gPhiNormal"] = mPhiNormal; + + mpFilterMoments->execute(pRenderContext, mpPingPongFbo[0]); +} + +void SVGFPass::computeAtrousDecomposition(RenderContext* pRenderContext, Texture::SharedPtr pAlbedoTexture) +{ + mpAtrous["gAlbedo"] = pAlbedoTexture; + mpAtrous["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); + mpAtrous["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); + + mpAtrous["PerImageCB"]["gPhiColor"] = mPhiColor; + mpAtrous["PerImageCB"]["gPhiNormal"] = mPhiNormal; + + for (int i = 0; i < mFilterIterations; i++) + { + Fbo::SharedPtr curTargetFbo = mpPingPongFbo[1]; + + mpAtrous["gIllumination"] = mpPingPongFbo[0]->getColorTexture(0); + mpAtrous["PerImageCB"]["gStepSize"] = 1 << i; + + mpAtrous->execute(pRenderContext, curTargetFbo); + + // store the filtered color for the feedback path + if (i == std::min(mFeedbackTap, mFilterIterations - 1)) + { + pRenderContext->blit(curTargetFbo->getColorTexture(0)->getSRV(), mpFilteredPastFbo->getRenderTargetView(0)); + } + + std::swap(mpPingPongFbo[0], mpPingPongFbo[1]); + } + + if (mFeedbackTap < 0) + { + pRenderContext->blit(mpCurReprojFbo->getColorTexture(0)->getSRV(), mpFilteredPastFbo->getRenderTargetView(0)); + } +} + +void SVGFPass::renderUI(Gui::Widgets& widget) +{ + int dirty = 0; + dirty |= (int)widget.checkbox(mFilterEnabled ? "SVGF enabled" : "SVGF disabled", mFilterEnabled); + + widget.text(""); + widget.text("Number of filter iterations. Which"); + widget.text(" iteration feeds into future frames?"); + dirty |= (int)widget.var("Iterations", mFilterIterations, 2, 10, 1); + dirty |= (int)widget.var("Feedback", mFeedbackTap, -1, mFilterIterations - 2, 1); + + widget.text(""); + widget.text("Contol edge stopping on bilateral fitler"); + dirty |= (int)widget.var("For Color", mPhiColor, 0.0f, 10000.0f, 0.01f); + dirty |= (int)widget.var("For Normal", mPhiNormal, 0.001f, 1000.0f, 0.2f); + + widget.text(""); + widget.text("How much history should be used?"); + widget.text(" (alpha; 0 = full reuse; 1 = no reuse)"); + dirty |= (int)widget.var("Alpha", mAlpha, 0.0f, 1.0f, 0.001f); + dirty |= (int)widget.var("Moments Alpha", mMomentsAlpha, 0.0f, 1.0f, 0.001f); + + if (dirty) mBuffersNeedClear = true; +} diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.h b/Source/RenderPasses/SVGFPass/SVGFPass.h new file mode 100644 index 000000000..00b0baa1d --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFPass.h @@ -0,0 +1,92 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" + +using namespace Falcor; + +class SVGFPass : public RenderPass, inherit_shared_from_this +{ +public: + using SharedPtr = std::shared_ptr; + using inherit_shared_from_this::shared_from_this; + + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual std::string getDesc() override { return "SVGF Denoising Pass"; } + virtual Dictionary getScriptingDictionary() override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void renderUI(Gui::Widgets& widget) override; + +private: + SVGFPass() = default; + + bool init(const Dictionary& dict); + void allocateFbos(uvec2 dim, RenderContext* pRenderContext); + void clearBuffers(RenderContext* pRenderContext, const RenderData& renderData); + + void computeLinearZAndNormal(RenderContext* pRenderContext, Texture::SharedPtr pLinearZTexture, + Texture::SharedPtr pWorldNormalTexture); + void computeReprojection(RenderContext* pRenderContext, Texture::SharedPtr pAlbedoTexture, + Texture::SharedPtr pColorTexture, Texture::SharedPtr pEmissionTexture, + Texture::SharedPtr pMotionVectorTexture, + Texture::SharedPtr pPositionNormalFwidthTexture, + Texture::SharedPtr pPrevLinearZAndNormalTexture); + void computeFilteredMoments(RenderContext* pRenderContext); + void computeAtrousDecomposition(RenderContext* pRenderContext, Texture::SharedPtr pAlbedoTexture); + + bool mBuffersNeedClear = false; + + // SVGF parameters + bool mFilterEnabled = true; + int32_t mFilterIterations = 4; + int32_t mFeedbackTap = 1; + float mVarainceEpsilon = 1e-4f; + float mPhiColor = 10.0f; + float mPhiNormal = 128.0f; + float mAlpha = 0.05f; + float mMomentsAlpha = 0.2f; + + // SVGF passes + FullScreenPass::SharedPtr mpPackLinearZAndNormal; + FullScreenPass::SharedPtr mpReprojection; + FullScreenPass::SharedPtr mpFilterMoments; + FullScreenPass::SharedPtr mpAtrous; + FullScreenPass::SharedPtr mpFinalModulate; + + // Intermediate framebuffers + Fbo::SharedPtr mpPingPongFbo[2]; + Fbo::SharedPtr mpLinearZAndNormalFbo; + Fbo::SharedPtr mpFilteredPastFbo; + Fbo::SharedPtr mpCurReprojFbo; + Fbo::SharedPtr mpPrevReprojFbo; + Fbo::SharedPtr mpFilteredIlluminationFbo; + Fbo::SharedPtr mpFinalFbo; +}; diff --git a/Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj b/Source/RenderPasses/SVGFPass/SVGFPass.vcxproj similarity index 65% rename from Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj rename to Source/RenderPasses/SVGFPass/SVGFPass.vcxproj index 0f180a93d..132df1489 100644 --- a/Samples/Core/SimpleDeferred/SimpleDeferred.vcxproj +++ b/Source/RenderPasses/SVGFPass/SVGFPass.vcxproj @@ -1,4 +1,4 @@ - + @@ -10,51 +10,57 @@ x64 + + {691F64DD-0941-49DE-B52E-949341192154} + Win32Proj + SVGFPass + 10.0.17763.0 + SVGFPass + + + + + + + + - + - + + + + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} - - + + + - - {6E7CBE80-7C06-485B-BEA7-08AEBFE53C22} - Win32Proj - SimpleDeferred - SimpleDeferred - 10.0.17763.0 - - - Application + DynamicLibrary true v141 Unicode + Data\RenderPasses\$(ProjectName) - Application + DynamicLibrary false v141 true Unicode + Data\RenderPasses\$(ProjectName) - - - - - - true @@ -68,7 +74,10 @@ Level3 Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows @@ -83,7 +92,10 @@ MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 Windows diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.vcxproj.filters b/Source/RenderPasses/SVGFPass/SVGFPass.vcxproj.filters new file mode 100644 index 000000000..32a51cf84 --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFPass.vcxproj.filters @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang b/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang new file mode 100644 index 000000000..4ebaac00d --- /dev/null +++ b/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang @@ -0,0 +1,251 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +__import Helpers; +__import ShaderCommon; +__import Shading; +__import Utils.Math.MathHelpers; + +#include "RenderPasses/SVGFPass/SVGFCommon.slang.h" + +// Workaround for isnan() not working in slang. +bool isNaN(float f) +{ + uint u = asuint(f) & ~0x80000000u; // clear out the sign bit + return (u > 0x7F800000); // greater than Inf is NaN +} + +cbuffer PerImageCB +{ + Texture2D gMotion; + Texture2D gPositionNormalFwidth; + Texture2D gColor; + Texture2D gAlbedo; + Texture2D gEmission; + Texture2D gPrevIllum; + Texture2D gPrevMoments; + Texture2D gLinearZAndNormal; + Texture2D gPrevLinearZAndNormal; + Texture2D gPrevHistoryLength; + + float gAlpha; + float gMomentsAlpha; +}; + +float3 demodulate(float3 c, float3 albedo) +{ + return c / max(albedo, float3(0.001, 0.001, 0.001)); +} + +bool isReprjValid(int2 coord, float Z, float Zprev, float fwidthZ, float3 normal, float3 normalPrev, float fwidthNormal) +{ + const int2 imageDim = getTextureDims(gColor, 0); + + // check whether reprojected pixel is inside of the screen + if (any(coord < int2(1,1)) || any(coord > imageDim - int2(1,1))) return false; + + // check if deviation of depths is acceptable + if (abs(Zprev - Z) / (fwidthZ + 1e-2f) > 10.f) return false; + + // check normals for compatibility + if (distance(normal, normalPrev) / (fwidthNormal + 1e-2) > 16.0) return false; + + return true; +} + +bool loadPrevData(float2 posH, out float4 prevIllum, out float2 prevMoments, out float historyLength) +{ + const int2 ipos = posH; + const float2 imageDim = float2(getTextureDims(gColor, 0)); + + const float2 motion = gMotion[ipos].xy; + const float normalFwidth = gPositionNormalFwidth[ipos].y; + + // +0.5 to account for texel center offset + const int2 iposPrev = int2(float2(ipos) + motion.xy * imageDim + float2(0.5,0.5)); + + float2 depth = gLinearZAndNormal[ipos].xy; + float3 normal = oct_to_ndir_snorm(gLinearZAndNormal[ipos].zw); + + prevIllum = float4(0,0,0,0); + prevMoments = float2(0,0); + + bool v[4]; + const float2 posPrev = floor(posH.xy) + motion.xy * imageDim; + const int2 offset[4] = { int2(0, 0), int2(1, 0), int2(0, 1), int2(1, 1) }; + + // check for all 4 taps of the bilinear filter for validity + bool valid = false; + for (int sampleIdx = 0; sampleIdx < 4; sampleIdx++) + { + int2 loc = int2(posPrev) + offset[sampleIdx]; + float2 depthPrev = gPrevLinearZAndNormal[loc].xy; + float3 normalPrev = oct_to_ndir_snorm(gPrevLinearZAndNormal[loc].zw); + + v[sampleIdx] = isReprjValid(iposPrev, depth.x, depthPrev.x, depth.y, normal, normalPrev, normalFwidth); + + valid = valid || v[sampleIdx]; + } + + if (valid) + { + float sumw = 0; + float x = frac(posPrev.x); + float y = frac(posPrev.y); + + // bilinear weights + const float w[4] = { (1 - x) * (1 - y), + x * (1 - y), + (1 - x) * y, + x * y }; + + // perform the actual bilinear interpolation + for (int sampleIdx = 0; sampleIdx < 4; sampleIdx++) + { + const int2 loc = int2(posPrev) + offset[sampleIdx]; + if (v[sampleIdx]) + { + prevIllum += w[sampleIdx] * gPrevIllum[loc]; + prevMoments += w[sampleIdx] * gPrevMoments[loc].xy; + sumw += w[sampleIdx]; + } + } + + // redistribute weights in case not all taps were used + valid = (sumw >= 0.01); + prevIllum = valid ? prevIllum / sumw : float4(0, 0, 0, 0); + prevMoments = valid ? prevMoments / sumw : float2(0, 0); + } + + if (!valid) // perform cross-bilateral filter in the hope to find some suitable samples somewhere + { + float nValid = 0.0; + + // this code performs a binary descision for each tap of the cross-bilateral filter + const int radius = 1; + for (int yy = -radius; yy <= radius; yy++) + { + for (int xx = -radius; xx <= radius; xx++) + { + const int2 p = iposPrev + int2(xx, yy); + const float2 depthFilter = gPrevLinearZAndNormal[p].xy; + const float3 normalFilter = oct_to_ndir_snorm(gPrevLinearZAndNormal[p].zw); + + if (isReprjValid(iposPrev, depth.x, depthFilter.x, depth.y, normal, normalFilter, normalFwidth)) + { + prevIllum += gPrevIllum[p]; + prevMoments += gPrevMoments[p].xy; + nValid += 1.0; + } + } + } + if (nValid > 0) + { + valid = true; + prevIllum /= nValid; + prevMoments /= nValid; + } + } + + if (valid) + { + // crude, fixme + historyLength = gPrevHistoryLength[iposPrev].x; + } + else + { + prevIllum = float4(0,0,0,0); + prevMoments = float2(0,0); + historyLength = 0; + } + + return valid; +} + +// not used currently +float computeVarianceScale(float numSamples, float loopLength, float alpha) +{ + const float aa = (1.0 - alpha) * (1.0 - alpha); + return (1.0 - pow(aa, min(loopLength, numSamples))) / (1.0 - aa); +} + +struct PS_OUT +{ + float4 OutIllumination : SV_TARGET0; + float2 OutMoments : SV_TARGET1; + float OutHistoryLength : SV_TARGET2; +}; + +PS_OUT main(FullScreenPassVsOut vsOut) +{ + const float4 posH = vsOut.posH; + const int2 ipos = posH.xy; + + float3 illumination = demodulate(gColor[ipos].rgb - gEmission[ipos].rgb, gAlbedo[ipos].rgb); + // Workaround path tracer bugs. TODO: remove this when we can. + if (isNaN(illumination.x) || isNaN(illumination.y) || isNaN(illumination.z)) + { + illumination = float3(0, 0, 0); + } + + float historyLength; + float4 prevIllumination; + float2 prevMoments; + bool success = loadPrevData(posH.xy, prevIllumination, prevMoments, historyLength); + historyLength = min(32.0f, success ? historyLength + 1.0f : 1.0f); + + // this adjusts the alpha for the case where insufficient history is available. + // It boosts the temporal accumulation to give the samples equal weights in + // the beginning. + const float alpha = success ? max(gAlpha, 1.0 / historyLength) : 1.0; + const float alphaMoments = success ? max(gMomentsAlpha, 1.0 / historyLength) : 1.0; + + // compute first two moments of luminance + float2 moments; + moments.r = luminance(illumination); + moments.g = moments.r * moments.r; + + float2 pm = moments; + + // temporal integration of the moments + moments = lerp(prevMoments, moments, alphaMoments); + + float variance = max(0.f, moments.g - moments.r * moments.r); + + //variance *= computeVarianceScale(16, 16, alpha); + + PS_OUT psOut; + // temporal integration of illumination + psOut.OutIllumination = lerp(prevIllumination, float4(illumination, 0), alpha); + // variance is propagated through the alpha channel + psOut.OutIllumination.a = variance; + psOut.OutMoments = moments; + psOut.OutHistoryLength = historyLength; + + return psOut; +} diff --git a/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.cpp b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.cpp new file mode 100644 index 000000000..b1f40e27d --- /dev/null +++ b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "TemporalDelayPass.h" + +extern "C" __declspec(dllexport) const char* getProjDir() +{ + return PROJECT_DIR; +} + +void regTemporalDelayPass(ScriptBindings::Module& m) +{ + auto c = m.class_("TemporalDelayPass"); + c.func_("delay", &TemporalDelayPass::getDelay); + c.func_("delay", &TemporalDelayPass::setDelay); +} + +extern "C" __declspec(dllexport) void getPasses(RenderPassLibrary& lib) +{ + lib.registerClass("TemporalDelayPass", TemporalDelayPass::kDesc, TemporalDelayPass::create); + ScriptBindings::registerBinding(regTemporalDelayPass); +} + +namespace +{ + const std::string kSrc = "src"; + const std::string kMaxDelay = "maxDelay"; + const std::string kDelay = "delay"; +} +const char* TemporalDelayPass::kDesc = "Delays frame rendering by a specified amount of frames"; + +TemporalDelayPass::TemporalDelayPass() {} + +TemporalDelayPass::SharedPtr TemporalDelayPass::create(RenderContext* pRenderContext, const Dictionary& dict) +{ + SharedPtr pPass = SharedPtr(new TemporalDelayPass()); + for (const auto& v : dict) + { + if (v.key() == kDelay) pPass->mDelay = (uint32_t) v.val(); + else logWarning("Unknown field `" + v.key() + "` in a TemporalDelayPass dictionary"); + } + return pPass; +} + +RenderPassReflection TemporalDelayPass::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + mReady = false; + if (compileData.connectedResources.getFieldCount() > 0) + { + const RenderPassReflection::Field* edge = compileData.connectedResources.getField(kSrc); + RenderPassReflection::Field::Type srcType = edge->getType(); + ResourceFormat srcFormat = edge->getFormat(); + uint32_t srcWidth = edge->getWidth(); + uint32_t srcHeight = edge->getHeight(); + uint32_t srcDepth = edge->getDepth(); + uint32_t srcSampleCount = edge->getSampleCount(); + uint32_t srcMipCount = edge->getMipCount(); + uint32_t srcArraySize = edge->getArraySize(); + + auto formatField = [=](RenderPassReflection::Field& f) { + return f.format(srcFormat).resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); + }; + + formatField(r.addInput(kSrc, "Current frame")); + formatField(r.addOutput(kMaxDelay, to_string(mDelay) + " frame(s) delayed")); + if (mDelay > 0) + { + for (uint32_t i = mDelay - 1; i > 0; --i) formatField(r.addOutput(kMaxDelay + "-" + to_string(i), to_string(mDelay - i) + " frame(s) delayed")); + formatField(r.addInternal(kMaxDelay + "-" + to_string(mDelay), "Internal copy of the current frame")); + } + mReady = true; + } + else + { + r.addInput(kSrc, "Current frame"); + r.addOutput(kMaxDelay, to_string(mDelay) + " frame(s) delayed"); + if (mDelay > 0) + { + for (uint32_t i = mDelay - 1; i > 0; --i) r.addOutput(kMaxDelay + "-" + to_string(i), to_string(mDelay - i) + " frame(s) delayed"); + r.addInternal(kMaxDelay + "-" + to_string(mDelay), "Internal copy of the current frame"); + } + } + return r; +} + +void TemporalDelayPass::compile(RenderContext* pContext, const CompileData& compileData) +{ + if (!mReady) throw(std::runtime_error("TemporalDelayPass::compile - missing incoming reflection information")); +} + +void TemporalDelayPass::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + if (mDelay == 0) + { + pRenderContext->copyResource(renderData[kMaxDelay].get(), renderData[kSrc].get()); + return; + } + for (uint32_t copyDst = 0; copyDst <= mDelay; ++copyDst) + { + uint32_t copySrc = copyDst + 1; + if (copyDst == 0) pRenderContext->copyResource(renderData[kMaxDelay].get(), renderData[kMaxDelay + "-" + to_string(copySrc)].get()); + else if (copyDst == mDelay) pRenderContext->copyResource(renderData[kMaxDelay + "-" + to_string(copyDst)].get(), renderData[kSrc].get()); + else pRenderContext->copyResource(renderData[kMaxDelay + "-" + to_string(copyDst)].get(), renderData[kMaxDelay + "-" + to_string(copySrc)].get()); + } +} + +Dictionary TemporalDelayPass::getScriptingDictionary() +{ + Dictionary d; + d[kDelay] = mDelay; + return d; +} + +void TemporalDelayPass::renderUI(Gui::Widgets& widget) +{ + if (widget.var("Delay", mDelay, 0u)) setDelay(mDelay); +} + +TemporalDelayPass& TemporalDelayPass::setDelay(uint32_t delay) +{ + mDelay = delay; + mPassChangedCB(); + return *this; +} diff --git a/Samples/Raytracing/PathTracer/PathTracer.h b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.h similarity index 64% rename from Samples/Raytracing/PathTracer/PathTracer.h rename to Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.h index 3e3776c92..456b795e1 100644 --- a/Samples/Raytracing/PathTracer/PathTracer.h +++ b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,26 +27,31 @@ ***************************************************************************/ #pragma once #include "Falcor.h" -#include "FalcorExperimental.h" using namespace Falcor; -class PathTracer : public Renderer +class TemporalDelayPass : public RenderPass { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onShutdown(SampleCallbacks* pSample) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onDataReload(SampleCallbacks* pSample) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; + using SharedPtr = std::shared_ptr; + static const char* kDesc; -private: - void toggleCameraPathState(); + /** Create a new object + */ + static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); + + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual Dictionary getScriptingDictionary() override; + virtual std::string getDesc() override { return kDesc; } + virtual void renderUI(Gui::Widgets& widget) override; - bool mDisableCameraPath = false; - FirstPersonCameraController mCamController; - RenderGraph::SharedPtr mpGraph; + uint32_t getDelay() { return mDelay; } + TemporalDelayPass& setDelay(uint32_t delay); + +private: + TemporalDelayPass(); + uint32_t mDelay = 1; + bool mReady = false; }; diff --git a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj similarity index 85% rename from Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj rename to Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj index be665d480..62a0ef969 100644 --- a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj +++ b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj @@ -11,25 +11,22 @@ - + - + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} - - - - {232CBBB4-33D9-4445-BB62-08A7E7700147} + {6B527E70-C2C6-4C87-BB7D-E1154F6A6FEF} Win32Proj FeatureDemo 10.0.17763.0 - SamplePassLibrary + TemporalDelayPass @@ -49,10 +46,10 @@ - + - + @@ -69,6 +66,8 @@ Disabled WIN32;PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) %(AdditionalIncludeDirectories) + stdcpp17 + true Windows @@ -85,6 +84,8 @@ true WIN32;PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) %(AdditionalIncludeDirectories) + stdcpp17 + true Windows diff --git a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj.filters b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj.filters similarity index 61% rename from Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj.filters rename to Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj.filters index 0daa1b81e..046cf3e47 100644 --- a/Samples/RenderGraph/SamplePassLibrary/SamplePassLibrary.vcxproj.filters +++ b/Source/RenderPasses/TemporalDelayPass/TemporalDelayPass.vcxproj.filters @@ -1,19 +1,14 @@  - + - + {af379eba-da22-4554-b098-3b5a60dd6590} - - - Data - - \ No newline at end of file diff --git a/Source/RenderPasses/TemporalDelayPass/Testing/testTemporalDelayPass.py b/Source/RenderPasses/TemporalDelayPass/Testing/testTemporalDelayPass.py new file mode 100644 index 000000000..31f0719a9 --- /dev/null +++ b/Source/RenderPasses/TemporalDelayPass/Testing/testTemporalDelayPass.py @@ -0,0 +1,22 @@ +def test_temporal_delay(): + imageLoader = RenderPass("ImageLoader", {'filename': 'smoke-puff.png', 'mips': False, 'srgb': True}) + depthPass = RenderPass("DepthPass") + forwardLightingPass = RenderPass("ForwardLightingPass") + temporalDelayPass = RenderPass("TemporalDelayPass", {"delay": 16}) + + graph = RenderGraph("Temporal Delay Graph") + graph.addPass(imageLoader, "ImageLoader") + graph.addPass(depthPass, "DepthPass") + graph.addPass(forwardLightingPass, "ForwardLightingPass") + graph.addPass(temporalDelayPass, "TemporalDelayPass") + + graph.addEdge("ImageLoader.dst", "ForwardLightingPass.color") + graph.addEdge("DepthPass.depth", "ForwardLightingPass.depth") + graph.addEdge("ForwardLightingPass.color", "TemporalDelayPass.src") + graph.markOutput("TemporalDelayPass.maxDelay") + + return graph + +temporal_delay_graph = test_temporal_delay() + +m.addGraph(temporal_delay_graph) diff --git a/Source/RenderPasses/make_new_pass_project.py b/Source/RenderPasses/make_new_pass_project.py new file mode 100644 index 000000000..6a389ebc2 --- /dev/null +++ b/Source/RenderPasses/make_new_pass_project.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +import shutil +import sys +import os + +if len(sys.argv) != 2: + print ('Usage: make_new_project ') + sys.exit(1) + +# copy the make_new_pass_project directory +Src = "PassLibraryTemplate" +Dst = sys.argv[1] + +os.mkdir(Dst) + +Files=[] +Files.append(".cpp") +Files.append(".h") +Files.append(".vcxproj") +Files.append(".vcxproj.filters") + +for File in Files: + #rename the File + SrcFile = Src + '/' + Src + File + DstFile = Dst + '/' + Dst + File + + # replace all occurences + F = open(SrcFile) + Content = F.read() + F.close() + F = open(DstFile, 'w') + Content = Content.replace(Src, Dst); + F.write(Content.replace("RenderPassTemplate", Dst)) + F.close() diff --git a/Samples/Core/StereoRendering/Data/StereoRendering.gs.hlsl b/Source/Samples/CudaInterop/CopySurface.cu similarity index 60% rename from Samples/Core/StereoRendering/Data/StereoRendering.gs.hlsl rename to Source/Samples/CudaInterop/CopySurface.cu index 810d4ed11..fa0311eff 100644 --- a/Samples/Core/StereoRendering/Data/StereoRendering.gs.hlsl +++ b/Source/Samples/CudaInterop/CopySurface.cu @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,45 +25,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import ShaderCommon; -__import DefaultVS; +#include "CopySurface.h" -struct GeometryOut +// The CUDA kernel. This sample simply copies the input surface. +template +__global__ void copySurface(cudaSurfaceObject_t input, cudaSurfaceObject_t output, unsigned int width, unsigned int height) { - VertexOut vsOut; - uint rtIndex : SV_RenderTargetArrayIndex; -}; - -[maxvertexcount(6)] -void main(triangle VertexOut input[3], inout TriangleStream outStream) -{ - GeometryOut gsOut; - - // Left Eye - for (int i = 0; i < 3; i++) + unsigned int x = blockIdx.x * blockDim.x + threadIdx.x; + unsigned int y = blockIdx.y * blockDim.y + threadIdx.y; + if (x < width && y < height) { - gsOut.rtIndex = 0; - gsOut.vsOut = input[i]; - - float4 posW = float4(input[i].posW, 1.0f); - gsOut.vsOut.posH = mul(posW, gCamera.viewProjMat); - gsOut.vsOut.prevPosH = mul(posW, gCamera.prevViewProjMat); - - outStream.Append(gsOut); + T data; + surf2Dread(&data, input, sizeof(T) * x, y); + surf2Dwrite(data, output, sizeof(T) * x, y); } - outStream.RestartStrip(); - - // Right Eye - for (int i = 0; i < 3; i++) - { - gsOut.rtIndex = 1; - gsOut.vsOut = input[i]; - - float4 posW = float4(input[i].posW, 1.0f); - gsOut.vsOut.posH = mul(posW, gCamera.rightEyeViewProjMat); - gsOut.vsOut.prevPosH = mul(posW, gCamera.rightEyePrevViewProjMat); +} - outStream.Append(gsOut); - } - outStream.RestartStrip(); +// A wrapper function that launches the kernel. +void launchCopySurface(cudaSurfaceObject_t input, cudaSurfaceObject_t output, unsigned int width, unsigned int height, unsigned int format) +{ + dim3 dimBlock(16, 16); + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y - 1) / dimBlock.y); + if (format == cudaChannelFormatKindFloat) copySurface<<>>(input, output, width, height); + else copySurface<<>>(input, output, width, height); } diff --git a/Source/Samples/CudaInterop/CopySurface.h b/Source/Samples/CudaInterop/CopySurface.h new file mode 100644 index 000000000..ff6cedbf6 --- /dev/null +++ b/Source/Samples/CudaInterop/CopySurface.h @@ -0,0 +1,29 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +extern void launchCopySurface(cudaSurfaceObject_t input, cudaSurfaceObject_t output, unsigned int width, unsigned int height, unsigned int format); diff --git a/Source/Samples/CudaInterop/CudaInterop.cpp b/Source/Samples/CudaInterop/CudaInterop.cpp new file mode 100644 index 000000000..1e7b8d780 --- /dev/null +++ b/Source/Samples/CudaInterop/CudaInterop.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "CudaInterop.h" +#include "CopySurface.h" + +void CudaInterop::onLoad(RenderContext* pRenderContext) +{ + // Create our input and output textures + mpInputTex = Texture::createFromFile("smoke-puff.png", false, false, ResourceBindFlags::Shared); + mWidth = mpInputTex->getWidth(); + mHeight = mpInputTex->getHeight(); + mpOutputTex = Texture::create2D(mWidth, mHeight, mpInputTex->getFormat(), 1, 1, nullptr, ResourceBindFlags::Shared | ResourceBindFlags::ShaderResource); + + // Define our usage flags and then map the textures to CUDA surfaces. Surface values of 0 + // indicate an error during mapping. We need to cache mInputSurf and mOutputSurf as + // mapTextureToSurface() can only be called once per resource. + uint32_t usageFlags = cudaArrayColorAttachment; + mInputSurf = FalcorCUDA::mapTextureToSurface(mpInputTex, usageFlags); + if (mInputSurf == 0) + { + logError("Input texture to surface mapping failed"); + return; + } + mOutputSurf = FalcorCUDA::mapTextureToSurface(mpOutputTex, usageFlags); + if (mOutputSurf == 0) + { + logError("Output texture to surface mapping failed"); + return; + } +} + +void CudaInterop::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +{ + const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); + pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); + + // Call the CUDA kernel + uint32_t format = (getFormatType(mpInputTex->getFormat()) == FormatType::Float) ? cudaChannelFormatKindFloat : cudaChannelFormatKindUnsigned; + launchCopySurface(mInputSurf, mOutputSurf, mWidth, mHeight, format); + pRenderContext->blit(mpOutputTex->getSRV(), pTargetFbo->getRenderTargetView(0)); +} + +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) +{ + // Initializes the CUDA driver API, which is required prior to any API calls. + if (!FalcorCUDA::initCUDA()) + { + logError("CUDA driver API initialization failed"); + return -1; + } + CudaInterop::UniquePtr pRenderer = std::make_unique(); + SampleConfig config; + config.windowDesc.title = "Falcor-Cuda Interop"; + config.windowDesc.resizableWindow = true; + Sample::run(config, pRenderer); + return 0; +} diff --git a/Source/Samples/CudaInterop/CudaInterop.h b/Source/Samples/CudaInterop/CudaInterop.h new file mode 100644 index 000000000..3890552ba --- /dev/null +++ b/Source/Samples/CudaInterop/CudaInterop.h @@ -0,0 +1,48 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "cuda.h" +#include "Falcor.h" +#include "FalcorCUDA.h" + +using namespace Falcor; + +class CudaInterop : public IRenderer +{ +public: + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + +private: + uint32_t mWidth; + uint32_t mHeight; + Texture::SharedPtr mpInputTex; + Texture::SharedPtr mpOutputTex; + cudaSurfaceObject_t mInputSurf; + cudaSurfaceObject_t mOutputSurf; +}; diff --git a/Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj b/Source/Samples/CudaInterop/CudaInterop.vcxproj similarity index 60% rename from Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj rename to Source/Samples/CudaInterop/CudaInterop.vcxproj index f22ebe62b..c6340901b 100644 --- a/Samples/Effects/HDRToneMapping/HDRToneMapping.vcxproj +++ b/Source/Samples/CudaInterop/CudaInterop.vcxproj @@ -1,4 +1,4 @@ - + @@ -10,93 +10,96 @@ x64 + + + + + + + + + + + + + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + - {0A6AC638-6567-49F9-B328-66BA201C74B6} - Win32Proj - PostProcess + {B80C5BB4-A82E-4BAC-BF95-910AED1947AF} + CudaInterop 10.0.17763.0 - HDRToneMapping Application true + MultiByte v141 - Unicode Application false - v141 true - Unicode + MultiByte + v141 + - + - + + - + - + + true - - false - - - Level3 Disabled - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - Windows true + Windows + cudart_static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + 64 + Level3 - - MaxSpeed true true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - Windows true true true + Windows + cudart_static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + 64 + - - - - - - - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - - - - true - true - - + \ No newline at end of file diff --git a/Source/Samples/CudaInterop/FalcorCUDA.cpp b/Source/Samples/CudaInterop/FalcorCUDA.cpp new file mode 100644 index 000000000..f5a6ceb91 --- /dev/null +++ b/Source/Samples/CudaInterop/FalcorCUDA.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "FalcorCUDA.h" +#include +#include +#include +#include "Core/API/Device.h" + +#define CU_CHECK_SUCCESS(x) \ + do { \ + CUresult result = x; \ + if (result != CUDA_SUCCESS) \ + { \ + const char *msg; \ + cuGetErrorName(result, &msg); \ + Falcor::msgBox(std::string("CUDA Error: " #x " failed with error ") + msg); \ + return 0; \ + } \ + } while(0) + +#define CUDA_CHECK_SUCCESS(x) \ + do { \ + cudaError_t result = x; \ + if (result != cudaSuccess) \ + { \ + Falcor::msgBox(std::string("CUDA Error: " #x " failed with error ") + cudaGetErrorString(result)); \ + return 0; \ + } \ + } while(0) + +using namespace Falcor; + +namespace +{ + class WindowsSecurityAttributes + { + protected: + SECURITY_ATTRIBUTES mWinSecurityAttributes; + PSECURITY_DESCRIPTOR mWinPSecurityDescriptor; + + public: + WindowsSecurityAttributes::WindowsSecurityAttributes() + { + mWinPSecurityDescriptor = (PSECURITY_DESCRIPTOR) calloc(1, SECURITY_DESCRIPTOR_MIN_LENGTH + 2 * sizeof(void**)); + assert(mWinPSecurityDescriptor != (PSECURITY_DESCRIPTOR) NULL); + + PSID* ppSID = (PSID*) ((PBYTE)mWinPSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + PACL* ppACL = (PACL*) ((PBYTE)ppSID + sizeof(PSID *)); + + InitializeSecurityDescriptor(mWinPSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); + + SID_IDENTIFIER_AUTHORITY sidIdentifierAuthority = SECURITY_WORLD_SID_AUTHORITY; + AllocateAndInitializeSid(&sidIdentifierAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, ppSID); + + EXPLICIT_ACCESS explicitAccess; + ZeroMemory(&explicitAccess, sizeof(EXPLICIT_ACCESS)); + explicitAccess.grfAccessPermissions = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL; + explicitAccess.grfAccessMode = SET_ACCESS; + explicitAccess.grfInheritance = INHERIT_ONLY; + explicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; + explicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + explicitAccess.Trustee.ptstrName = (LPTSTR)*ppSID; + + SetEntriesInAcl(1, &explicitAccess, NULL, ppACL); + + SetSecurityDescriptorDacl(mWinPSecurityDescriptor, TRUE, *ppACL, FALSE); + + mWinSecurityAttributes.nLength = sizeof(mWinSecurityAttributes); + mWinSecurityAttributes.lpSecurityDescriptor = mWinPSecurityDescriptor; + mWinSecurityAttributes.bInheritHandle = TRUE; + } + + WindowsSecurityAttributes::~WindowsSecurityAttributes() + { + PSID* ppSID = (PSID*)((PBYTE)mWinPSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + PACL* ppACL = (PACL*)((PBYTE)ppSID + sizeof(PSID*)); + + if (*ppSID) FreeSid(*ppSID); + if (*ppACL) LocalFree(*ppACL); + free(mWinPSecurityDescriptor); + } + SECURITY_ATTRIBUTES * operator&() { return &mWinSecurityAttributes; } + }; + + uint32_t gNodeMask; + CUdevice gCudaDevice; + CUcontext gCudaContext; + CUstream gCudaStream; + +} + +namespace FalcorCUDA +{ + bool initCUDA() + { + CU_CHECK_SUCCESS(cuInit(0)); + int32_t firstGPUID = -1; + cudaDeviceProp prop; + int32_t count; + cudaError_t err = cudaGetDeviceCount(&count); + + for (int32_t i = 0; i < count; ++i) + { + err = cudaGetDeviceProperties(&prop, i); + if (prop.major >= 3) + { + firstGPUID = i; + break; + } + } + + if (firstGPUID < 0) + { + Falcor::msgBox("No CUDA 10 compatible GPU found"); + return false; + } + gNodeMask = prop.luidDeviceNodeMask; + CUDA_CHECK_SUCCESS(cudaSetDevice(firstGPUID)); + CU_CHECK_SUCCESS(cuDeviceGet(&gCudaDevice, firstGPUID)); + CU_CHECK_SUCCESS(cuCtxCreate(&gCudaContext, 0, gCudaDevice)); + CU_CHECK_SUCCESS(cuStreamCreate(&gCudaStream, CU_STREAM_DEFAULT)); + return true; + } + + bool importTextureToMipmappedArray(Falcor::Texture::SharedPtr pTex, cudaMipmappedArray_t & mipmappedArray, uint32_t cudaUsageFlags) + { + HANDLE sharedHandle = pTex->createSharedApiHandle(); + if (sharedHandle == NULL) + { + logError("FalcorCUDA::importTextureToMipmappedArray - texture shared handle creation failed"); + return false; + } + + cudaExternalMemoryHandleDesc externalMemoryHandleDesc; + memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc)); + + externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeD3D12Resource; + externalMemoryHandleDesc.handle.win32.handle = sharedHandle; + externalMemoryHandleDesc.size = pTex->getTextureSizeInBytes(); + externalMemoryHandleDesc.flags = cudaExternalMemoryDedicated; + + cudaExternalMemory_t externalMemory; + CUDA_CHECK_SUCCESS(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); + + // Map mipmapped array onto external memory + cudaExternalMemoryMipmappedArrayDesc mipDesc; + memset(&mipDesc, 0, sizeof(mipDesc)); + auto format = pTex->getFormat(); + mipDesc.formatDesc.x = getNumChannelBits(format, 0); + mipDesc.formatDesc.y = getNumChannelBits(format, 1); + mipDesc.formatDesc.z = getNumChannelBits(format, 2); + mipDesc.formatDesc.w = getNumChannelBits(format, 3); + mipDesc.formatDesc.f = (getFormatType(format) == FormatType::Float) ? cudaChannelFormatKindFloat : cudaChannelFormatKindUnsigned; + mipDesc.extent.depth = 1; + mipDesc.extent.width = pTex->getWidth(); + mipDesc.extent.height = pTex->getHeight(); + mipDesc.flags = cudaUsageFlags; + mipDesc.numLevels = 1; + + CUDA_CHECK_SUCCESS(cudaExternalMemoryGetMappedMipmappedArray(&mipmappedArray, externalMemory, &mipDesc)); + return true; + } + + cudaSurfaceObject_t mapTextureToSurface(Texture::SharedPtr pTex, uint32_t cudaUsageFlags) + { + // Create a mipmapped array from the texture + cudaMipmappedArray_t mipmap; + if (!importTextureToMipmappedArray(pTex, mipmap, cudaUsageFlags)) + { + logError("Failed to import texture into a mipmapped array"); + return 0; + } + + // Grab level 0 + cudaArray_t cudaArray; + CUDA_CHECK_SUCCESS(cudaGetMipmappedArrayLevel(&cudaArray, mipmap, 0)); + + // Create cudaSurfObject_t from CUDA array + cudaResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.res.array.array = cudaArray; + resDesc.resType = cudaResourceTypeArray; + + cudaSurfaceObject_t surface; + CUDA_CHECK_SUCCESS(cudaCreateSurfaceObject(&surface, &resDesc)); + return surface; + } +} diff --git a/Framework/Source/Graphics/TextureHelper.h b/Source/Samples/CudaInterop/FalcorCUDA.h similarity index 52% rename from Framework/Source/Graphics/TextureHelper.h rename to Source/Samples/CudaInterop/FalcorCUDA.h index 81461aa7e..f989e7c3c 100644 --- a/Framework/Source/Graphics/TextureHelper.h +++ b/Source/Samples/CudaInterop/FalcorCUDA.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,22 +26,31 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #pragma once -#include -#include "API/Texture.h" -namespace Falcor +#include "cuda_runtime.h" +#include "Core/Framework.h" +#include "Core/API/Texture.h" +#include "Core/API/RenderContext.h" + +namespace FalcorCUDA { - /*! - * \addtogroup Falcor - * @{ + /** Initializes the CUDA driver API. Returns true if successful, false otherwise. */ + bool initCUDA(); - /** Create a new texture object from a file. - \param[in] filename Filename of the image. Can also include a full path or relative path from a data directory - \param[in] generateMipLevels Whether the mip-chain should be generated - \param[in] loadAsSrgb Load the texture using sRGB format. Only valid for 3 or 4 component textures. - \param[in] bindFlags The bind flags to create the texture with + /** Imports the texture into a CUDA mipmapped array and returns the array in mipmappedArray. This method should only be called once per + texture resource. + \param pTex Pointer to the texture being imported + \param mipmappedArray Reference to the array to import to + \param usageFlags The requested flags to be bound to the mipmapped array + \return True if successful, false otherwise */ - Texture::SharedPtr createTextureFromFile(const std::string& filename, bool generateMipLevels, bool loadAsSrgb, Texture::BindFlags bindFlags = Texture::BindFlags::ShaderResource); + bool importTextureToMipmappedArray(Falcor::Texture::SharedPtr pTex, cudaMipmappedArray_t & mipmappedArray, uint32_t cudaUsageFlags); - /*! @} */ -} \ No newline at end of file + /** Maps a texture to a surface object which can be read and written within a CUDA kernel. + This method should only be called once per texture on initial load. Store the returned surface object for repeated use. + \param pTex Pointer to the texture being mapped + \param usageFlags The requested flags to be bound to the underlying mipmapped array that will be used to create the surface object + \return The surface object that the input texture is bound to + */ + cudaSurfaceObject_t mapTextureToSurface(Falcor::Texture::SharedPtr pTex, uint32_t usageFlags); +}; diff --git a/Source/Samples/CudaInterop/FalcorCUDA.props b/Source/Samples/CudaInterop/FalcorCUDA.props new file mode 100644 index 000000000..cf291288f --- /dev/null +++ b/Source/Samples/CudaInterop/FalcorCUDA.props @@ -0,0 +1,16 @@ + + + + + + + + $(CUDA_PATH)\include;%(AdditionalIncludeDirectories) + + + $(CUDA_PATH)\lib\x64;%(AdditionalLibraryDirectories) + cuda.lib;%(AdditionalDependencies) + + + + \ No newline at end of file diff --git a/Source/Samples/CudaInterop/README.md b/Source/Samples/CudaInterop/README.md new file mode 100644 index 000000000..56d470d49 --- /dev/null +++ b/Source/Samples/CudaInterop/README.md @@ -0,0 +1,7 @@ +# Using the CUDA interop with a project + +In order to use the CUDA interop, the following steps will need to be completed: +1. In Visual Studio, make a new CUDA Runtime project and add it to Falcor. (You must have the CUDA Toolkit installed to do so.) +2. Right-click on References under the new project in the Solution Explorer, select Add Reference, and add Falcor. +3. Open the Property Manager and add the Falcor and FalcorCUDA property sheets to both Debug and Release. +4. Open the project properties. If the project will produce a Windows application, go to General -> Configuration Type and change the setting to **Application (.exe)**, and go to Linker -> System -> SubSystem and change the setting to **Windows**. If the project is a DLL, then only the Configuration Type will need to be changed to **Dynamic Library (.dll)**. \ No newline at end of file diff --git a/Samples/Raytracing/HelloDXR/Data/HelloDXR.ps.hlsl b/Source/Samples/HelloDXR/Data/HelloDXR.ps.hlsl similarity index 83% rename from Samples/Raytracing/HelloDXR/Data/HelloDXR.ps.hlsl rename to Source/Samples/HelloDXR/Data/HelloDXR.ps.hlsl index d2d1db633..181f7acb0 100644 --- a/Samples/Raytracing/HelloDXR/Data/HelloDXR.ps.hlsl +++ b/Source/Samples/HelloDXR/Data/HelloDXR.ps.hlsl @@ -25,21 +25,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import ShaderCommon; -__import Shading; -__import DefaultVS; +import ShaderCommon; +import Shading; +import Raster; +import Scene; -float4 main(VertexOut vOut) : SV_TARGET +float4 main(VSOut vOut, uint triangleIndex : SV_PrimitiveID) : SV_TARGET { - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); + ShadingData sd = prepareShadingData(vOut, triangleIndex, gScene.camera.posW); float4 color = 0; color.a = 1; - [unroll] - for (uint i = 0; i < 3; i++) + for (int i = 0; i < gScene.getLightCount(); i++) { - color += evalMaterial(sd, gLights[i], 1).color; + color.rgb += evalMaterial(sd, gScene.getLight(i), 1).color.rgb; } + color.rgb += sd.emissive; return color; -} \ No newline at end of file +} diff --git a/Samples/Raytracing/HelloDXR/Data/HelloDXR.rt.hlsl b/Source/Samples/HelloDXR/Data/HelloDXR.rt.hlsl similarity index 75% rename from Samples/Raytracing/HelloDXR/Data/HelloDXR.rt.hlsl rename to Source/Samples/HelloDXR/Data/HelloDXR.rt.hlsl index c2fd200d2..d147751a3 100644 --- a/Samples/Raytracing/HelloDXR/Data/HelloDXR.rt.hlsl +++ b/Source/Samples/HelloDXR/Data/HelloDXR.rt.hlsl @@ -26,14 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ RWTexture2D gOutput; -__import Raytracing; +import Raytracing; +import Helpers; +import Scene; shared cbuffer PerFrameCB { float4x4 invView; - float4x4 invModel; float2 viewportDims; float tanHalfFovY; + uint sampleIndex; + bool useDOF; }; struct PrimaryRayData @@ -69,7 +72,7 @@ void primaryMiss(inout PrimaryRayData hitData) bool checkLightHit(uint lightIndex, float3 origin) { - float3 direction = gLights[lightIndex].posW - origin; + float3 direction = gScene.lights[lightIndex].posW - origin; RayDesc ray; ray.Origin = origin; ray.Direction = normalize(direction); @@ -82,7 +85,7 @@ bool checkLightHit(uint lightIndex, float3 origin) return rayData.hit; } -float3 getReflectionColor(float3 worldOrigin, VertexOut v, float3 worldRayDir, uint hitDepth) +float3 getReflectionColor(float3 worldOrigin, VertexData v, float3 worldRayDir, uint hitDepth) { float3 reflectColor = float3(0, 0, 0); if (hitDepth == 0) @@ -113,27 +116,27 @@ void primaryClosestHit(inout PrimaryRayData hitData, in BuiltInTriangleIntersect float3 posW = rayOrigW + hitT * rayDirW; // prepare the shading data - VertexOut v = getVertexAttributes(triangleIndex, attribs); - ShadingData sd = prepareShadingData(v, gMaterial, rayOrigW, 0); + VertexData v = getVertexData(triangleIndex, attribs); + ShadingData sd = prepareShadingData(v, gScene.materials[gScene.getMaterialID(getGlobalHitID())], rayOrigW, 0); // Shoot a reflection ray float3 reflectColor = getReflectionColor(posW, v, rayDirW, hitData.depth.r); float3 color = 0; [unroll] - for (int i = 0; i < gLightsCount; i++) - { + for (int i = 0; i < gScene.getLightCount(); i++) + { if (checkLightHit(i, posW) == false) { - color += evalMaterial(sd, gLights[i], 1).color.xyz; + color += evalMaterial(sd, gScene.getLight(i), 1).color.xyz; } } hitData.color.rgb = color; hitData.hitT = hitT; // A very non-PBR inaccurate way to do reflections - float roughness = min(0.5, max(1e-8, sd.roughness)); - hitData.color.rgb += sd.specular * reflectColor * (roughness * roughness); + float alpha = min(0.5, max(1e-8, sd.ggxAlpha)); + hitData.color.rgb += sd.specular * reflectColor * (alpha * alpha); hitData.color.rgb += sd.emissive; hitData.color.a = 1; } @@ -142,22 +145,17 @@ void primaryClosestHit(inout PrimaryRayData hitData, in BuiltInTriangleIntersect void rayGen() { uint3 launchIndex = DispatchRaysIndex(); - float2 d = (((launchIndex.xy + 0.5) / viewportDims) * 2.f - 1.f); - float aspectRatio = viewportDims.x / viewportDims.y; + uint randSeed = rand_init(launchIndex.x + launchIndex.y * viewportDims.x, sampleIndex, 16); RayDesc ray; - ray.Origin = invView[3].xyz; - - // We negate the Z exis because the 'view' matrix is generated using a - // Right Handed coordinate system with Z pointing towards the viewer - // The negation of Z axis is needed to get the rays go out in the direction away fromt he viewer. - // The negation of Y axis is needed because the texel coordinate system, used in the UAV we write into using launchIndex - // has the Y axis flipped with respect to the camera Y axis (0 is at the top and 1 at the bottom) - ray.Direction = normalize( (d.x * invView[0].xyz * tanHalfFovY * aspectRatio) - (d.y * invView[1].xyz * tanHalfFovY) - invView[2].xyz ); - - ray.TMin = 0; - ray.TMax = 100000; - + if (!useDOF) + { + ray = generateRay(gScene.camera, launchIndex.xy, viewportDims); + } + else + { + ray = generateDOFRay(gScene.camera, launchIndex.xy, viewportDims, randSeed); + } PrimaryRayData hitData; hitData.depth = 0; TraceRay( gRtScene, 0 /*rayFlags*/, 0xFF, 0 /* ray index*/, hitProgramCount, 0, ray, hitData ); diff --git a/Samples/Raytracing/HelloDXR/HelloDXR.cpp b/Source/Samples/HelloDXR/HelloDXR.cpp similarity index 62% rename from Samples/Raytracing/HelloDXR/HelloDXR.cpp rename to Source/Samples/HelloDXR/HelloDXR.cpp index eabf8bbb4..5a8baa871 100644 --- a/Samples/Raytracing/HelloDXR/HelloDXR.cpp +++ b/Source/Samples/HelloDXR/HelloDXR.cpp @@ -37,87 +37,63 @@ std::string to_string(const vec3& v) return s; } -void HelloDXR::onGuiRender(SampleCallbacks* pSample, Gui* pGui) +void HelloDXR::onGuiRender(Gui* pGui) { - pGui->addCheckBox("Ray Trace", mRayTrace); - if (pGui->addButton("Load Scene")) + Gui::Window w(pGui, "Hello DXR Settings", { 300, 400 }, { 10, 80 }); + + w.checkbox("Ray Trace", mRayTrace); + w.checkbox("Use Depth of Field", mUseDOF); + if (w.button("Load Scene")) { std::string filename; if (openFileDialog(Scene::kFileExtensionFilters, filename)) { - loadScene(filename, pSample->getCurrentFbo().get()); + loadScene(filename, gpFramework->getTargetFbo().get()); } } - for(uint32_t i = 0 ; i < mpScene->getLightCount() ; i++) - { - std::string group = "Point Light" + std::to_string(i); - mpScene->getLight(i)->renderUI(pGui, group.c_str()); - } + mpScene->renderUI(w); } void HelloDXR::loadScene(const std::string& filename, const Fbo* pTargetFbo) { - mpScene = RtScene::loadFromFile(filename, RtBuildFlags::None, Model::LoadFlags::RemoveInstancing); - Model::SharedPtr pModel = mpScene->getModel(0); - float radius = pModel->getRadius(); - - mpCamera = mpScene->getActiveCamera(); - assert(mpCamera); - - mCamController.attachCamera(mpCamera); + mpScene = Scene::create(filename); + if (!mpScene) return; - Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - Sampler::SharedPtr pSampler = Sampler::create(samplerDesc); - pModel->bindSamplerToMaterials(pSampler); + mpCamera = mpScene->getCamera(); // Update the controllers - mCamController.setCameraSpeed(radius * 0.25f); - float nearZ = std::max(0.1f, pModel->getRadius() / 750.0f); + float radius = length(mpScene->getSceneBounds().extent); + mpScene->setCameraSpeed(radius * 0.25f); + float nearZ = std::max(0.1f, radius / 750.0f); float farZ = radius * 10; mpCamera->setDepthRange(nearZ, farZ); mpCamera->setAspectRatio((float)pTargetFbo->getWidth() / (float)pTargetFbo->getHeight()); - mpSceneRenderer = SceneRenderer::create(mpScene); - mpRtVars = RtProgramVars::create(mpRaytraceProgram, mpScene); - mpRtRenderer = RtSceneRenderer::create(mpScene); -} -void HelloDXR::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) -{ - if (gpDevice->isFeatureSupported(Device::SupportedFeatures::Raytracing) == false) - { - logErrorAndExit("Device does not support raytracing!", true); - } + mpRasterPass = RasterScenePass::create(mpScene, "HelloDXR.ps.hlsl", "", "main"); RtProgram::Desc rtProgDesc; rtProgDesc.addShaderLibrary("HelloDXR.rt.hlsl").setRayGen("rayGen"); rtProgDesc.addHitGroup(0, "primaryClosestHit", "").addMiss(0, "primaryMiss"); rtProgDesc.addHitGroup(1, "", "shadowAnyHit").addMiss(1, "shadowMiss"); + rtProgDesc.addDefines(mpScene->getSceneDefines()); mpRaytraceProgram = RtProgram::create(rtProgDesc); - - mpRasterProgram = GraphicsProgram::createFromFile("HelloDXR.ps.hlsl", "", "main"); - - loadScene(kDefaultScene, pSample->getCurrentFbo().get()); - - mpProgramVars = GraphicsVars::create(mpRasterProgram->getReflector()); - mpGraphicsState = GraphicsState::create(); - mpGraphicsState->setProgram(mpRasterProgram); + mpRtVars = RtProgramVars::create(mpRaytraceProgram, mpScene); mpRtState = RtState::create(); mpRtState->setProgram(mpRaytraceProgram); mpRtState->setMaxTraceRecursionDepth(3); // 1 for calling TraceRay from RayGen, 1 for calling it from the primary-ray ClosestHitShader for reflections, 1 for reflection ray tracing a shadow ray } -void HelloDXR::renderRaster(RenderContext* pContext) +void HelloDXR::onLoad(RenderContext* pRenderContext) { - mpGraphicsState->setRasterizerState(nullptr); - mpGraphicsState->setDepthStencilState(nullptr); - mpGraphicsState->setProgram(mpRasterProgram); - pContext->setGraphicsState(mpGraphicsState); - pContext->setGraphicsVars(mpProgramVars); - mpSceneRenderer->renderScene(pContext, mpCamera.get()); + if (gpDevice->isFeatureSupported(Device::SupportedFeatures::Raytracing) == false) + { + logErrorAndExit("Device does not support raytracing!"); + } + + loadScene(kDefaultScene, gpFramework->getTargetFbo().get()); } void HelloDXR::setPerFrameVars(const Fbo* pTargetFbo) @@ -129,6 +105,8 @@ void HelloDXR::setPerFrameVars(const Fbo* pTargetFbo) pCB["viewportDims"] = vec2(pTargetFbo->getWidth(), pTargetFbo->getHeight()); float fovY = focalLengthToFovY(mpCamera->getFocalLength(), Camera::kDefaultFrameHeight); pCB["tanHalfFovY"] = tanf(fovY * 0.5f); + pCB["sampleIndex"] = mSampleIndex++; + pCB["useDOF"] = mUseDOF; } void HelloDXR::renderRT(RenderContext* pContext, const Fbo* pTargetFbo) @@ -139,57 +117,51 @@ void HelloDXR::renderRT(RenderContext* pContext, const Fbo* pTargetFbo) pContext->clearUAV(mpRtOut->getUAV().get(), kClearColor); mpRtVars->getRayGenVars()->setTexture("gOutput", mpRtOut); - mpRtRenderer->renderScene(pContext, mpRtVars, mpRtState, uvec3(pTargetFbo->getWidth(), pTargetFbo->getHeight(), 1), mpCamera.get()); + mpScene->raytrace(pContext, mpRtState, mpRtVars, uvec3(pTargetFbo->getWidth(), pTargetFbo->getHeight(), 1)); pContext->blit(mpRtOut->getSRV(), pTargetFbo->getRenderTargetView(0)); } -void HelloDXR::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void HelloDXR::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) { pRenderContext->clearFbo(pTargetFbo.get(), kClearColor, 1.0f, 0, FboAttachmentType::All); if(mpScene) { - mpGraphicsState->setFbo(pTargetFbo); - mCamController.update(); - - if (mRayTrace) - { - renderRT(pRenderContext, pTargetFbo.get()); - } - else - { - renderRaster(pRenderContext); - } + mpScene->update(pRenderContext, gpFramework->getGlobalClock().now()); + if (mRayTrace) renderRT(pRenderContext, pTargetFbo.get()); + else mpRasterPass->renderScene(pRenderContext, pTargetFbo); } + + TextRenderer::render(pRenderContext, gpFramework->getFrameRate().getMsg(), pTargetFbo, { 20, 20 }); } -bool HelloDXR::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) +bool HelloDXR::onKeyEvent(const KeyboardEvent& keyEvent) { - if (mCamController.onKeyEvent(keyEvent)) - { - return true; - } if (keyEvent.key == KeyboardEvent::Key::Space && keyEvent.type == KeyboardEvent::Type::KeyPressed) { mRayTrace = !mRayTrace; return true; } + if (mpScene && mpScene->onKeyEvent(keyEvent)) return true; return false; } -bool HelloDXR::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) +bool HelloDXR::onMouseEvent(const MouseEvent& mouseEvent) { - return mCamController.onMouseEvent(mouseEvent); + return mpScene && mpScene->onMouseEvent(mouseEvent); } -void HelloDXR::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) +void HelloDXR::onResizeSwapChain(uint32_t width, uint32_t height) { float h = (float)height; float w = (float)width; - mpCamera->setFocalLength(18); - float aspectRatio = (w / h); - mpCamera->setAspectRatio(aspectRatio); + if (mpCamera) + { + mpCamera->setFocalLength(18); + float aspectRatio = (w / h); + mpCamera->setAspectRatio(aspectRatio); + } mpRtOut = Texture::create2D(width, height, ResourceFormat::RGBA16Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); } diff --git a/Samples/Raytracing/HelloDXR/HelloDXR.h b/Source/Samples/HelloDXR/HelloDXR.h similarity index 68% rename from Samples/Raytracing/HelloDXR/HelloDXR.h rename to Source/Samples/HelloDXR/HelloDXR.h index 92129cbb8..7a1cf0094 100644 --- a/Samples/Raytracing/HelloDXR/HelloDXR.h +++ b/Source/Samples/HelloDXR/HelloDXR.h @@ -27,39 +27,36 @@ ***************************************************************************/ #pragma once #include "Falcor.h" -#include "FalcorExperimental.h" using namespace Falcor; -class HelloDXR : public Renderer +class HelloDXR : public IRenderer { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onResizeSwapChain(uint32_t width, uint32_t height) override; + bool onKeyEvent(const KeyboardEvent& keyEvent) override; + bool onMouseEvent(const MouseEvent& mouseEvent) override; + void onGuiRender(Gui* pGui) override; private: - RtScene::SharedPtr mpScene; - SceneRenderer::SharedPtr mpSceneRenderer; + RasterScenePass::SharedPtr mpRasterPass; + Scene::SharedPtr mpScene; RtProgram::SharedPtr mpRaytraceProgram = nullptr; - GraphicsProgram::SharedPtr mpRasterProgram = nullptr; - GraphicsVars::SharedPtr mpProgramVars = nullptr; - GraphicsState::SharedPtr mpGraphicsState = nullptr; Camera::SharedPtr mpCamera; - FirstPersonCameraController mCamController; bool mRayTrace = true; + bool mUseDOF = false; RtProgramVars::SharedPtr mpRtVars; RtState::SharedPtr mpRtState; - RtSceneRenderer::SharedPtr mpRtRenderer; + //RtSceneRenderer::SharedPtr mpRtRenderer; Texture::SharedPtr mpRtOut; + uint32_t mSampleIndex = 0xdeadbeef; + void setPerFrameVars(const Fbo* pTargetFbo); void renderRT(RenderContext* pContext, const Fbo* pTargetFbo); - void renderRaster(RenderContext* pContext); void loadScene(const std::string& filename, const Fbo* pTargetFbo); }; diff --git a/Samples/Raytracing/HelloDXR/HelloDXR.vcxproj b/Source/Samples/HelloDXR/HelloDXR.vcxproj similarity index 92% rename from Samples/Raytracing/HelloDXR/HelloDXR.vcxproj rename to Source/Samples/HelloDXR/HelloDXR.vcxproj index bd221b07b..9d3739e3b 100644 --- a/Samples/Raytracing/HelloDXR/HelloDXR.vcxproj +++ b/Source/Samples/HelloDXR/HelloDXR.vcxproj @@ -16,11 +16,6 @@ - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - true @@ -31,6 +26,11 @@ true + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + {71B60B71-89A2-4196-BFB9-4A848CF6C541} Win32Proj @@ -56,10 +56,10 @@ - + - + @@ -75,6 +75,8 @@ Level3 Disabled WIN32;SOLUTION_DIR=R"($(SolutionDir))";_DEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows @@ -92,6 +94,8 @@ true true WIN32;SOLUTION_DIR=R"($(SolutionDir))";NDEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows diff --git a/Samples/Raytracing/HelloDXR/HelloDXR.vcxproj.filters b/Source/Samples/HelloDXR/HelloDXR.vcxproj.filters similarity index 100% rename from Samples/Raytracing/HelloDXR/HelloDXR.vcxproj.filters rename to Source/Samples/HelloDXR/HelloDXR.vcxproj.filters diff --git a/Samples/Utils/ModelViewer/Data/ModelViewer.ps.hlsl b/Source/Samples/ModelViewer/Data/ModelViewer.ps.hlsl similarity index 78% rename from Samples/Utils/ModelViewer/Data/ModelViewer.ps.hlsl rename to Source/Samples/ModelViewer/Data/ModelViewer.ps.hlsl index 19195e134..c7d5e005f 100644 --- a/Samples/Utils/ModelViewer/Data/ModelViewer.ps.hlsl +++ b/Source/Samples/ModelViewer/Data/ModelViewer.ps.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,18 +25,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -__import Shading; -__import DefaultVS; +import Shading; +import Raster; +import Scene; cbuffer PerFrameCB : register(b0) { - LightData gDirLight; - LightData gPointLight; bool gConstColor; - float3 gAmbient; }; -float4 main(VertexOut vOut) : SV_TARGET +float4 main(VSOut vOut, float4 pixelCrd : SV_POSITION, uint triangleIndex : SV_PrimitiveID) : SV_TARGET { if(gConstColor) { @@ -44,11 +42,14 @@ float4 main(VertexOut vOut) : SV_TARGET } else { - ShadingData sd = prepareShadingData(vOut, gMaterial, gCamera.posW); - float4 finalColor; - finalColor.a = 1; - finalColor.rgb = evalMaterial(sd, gDirLight, 1).color.rgb; - finalColor.rgb += evalMaterial(sd, gPointLight, 1).color.rgb; + ShadingData sd = prepareShadingData(vOut, triangleIndex, gScene.camera.posW); + float4 finalColor = float4(0, 0, 0, 1); + + for (int i = 0; i < gScene.getLightCount(); i++) + { + finalColor.rgb += evalMaterial(sd, gScene.getLight(i), 1).color.rgb; + } + return finalColor; } } diff --git a/Source/Samples/ModelViewer/ModelViewer.cpp b/Source/Samples/ModelViewer/ModelViewer.cpp new file mode 100644 index 000000000..845e64d13 --- /dev/null +++ b/Source/Samples/ModelViewer/ModelViewer.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "ModelViewer.h" + +void ModelViewer::setModelString(double loadTime) +{ + assert(mpScene != nullptr); + + mModelString = "Loading took " + std::to_string(loadTime) + " seconds.\n"; + //mModelString += "Model has " + std::to_string(pModel->getVertexCount()) + " vertices, "; + //mModelString += std::to_string(pModel->getIndexCount()) + " indices, "; + //mModelString += std::to_string(pModel->getPrimitiveCount()) + " primitives, "; + mModelString += std::to_string(mpScene->getMeshCount()) + " meshes, "; + mModelString += std::to_string(mpScene->getMeshInstanceCount()) + " mesh instances, "; + mModelString += std::to_string(mpScene->getMaterialCount()) + " materials, "; + //mModelString += std::to_string(pModel->getTextureCount()) + " textures, "; + //mModelString += std::to_string(pModel->getBufferCount()) + " buffers.\n"; +} + +void ModelViewer::loadModelFromFile(const std::string& filename, ResourceFormat fboFormat) +{ + CpuTimer timer; + timer.update(); + + SceneBuilder::Flags flags = SceneBuilder::Flags::None; + if (mUseOriginalTangents) flags |= SceneBuilder::Flags::UseOriginalTangentSpace; + if (mRemoveDuplicateMaterials) flags |= SceneBuilder::Flags::RemoveDuplicateMaterials; + flags |= isSrgbFormat(fboFormat) ? SceneBuilder::Flags::None : SceneBuilder::Flags::AssumeLinearSpaceTextures; + + SceneBuilder::SharedPtr pBuilder = SceneBuilder::create(filename, flags); + + if(!pBuilder) + { + msgBox("Could not load model"); + return; + } + + mpScene = pBuilder->getScene(); + mpProgram->addDefines(mpScene->getSceneDefines()); + mpProgramVars = GraphicsVars::create(mpProgram->getReflector()); + mpScene->bindSamplerToMaterials(mUseTriLinearFiltering ? mpLinearSampler : mpPointSampler); + setCamController(); + + timer.update(); + setModelString(timer.delta()); +} + +void ModelViewer::loadModel(ResourceFormat fboFormat) +{ + std::string Filename; + if(openFileDialog(Scene::kFileExtensionFilters, Filename)) + { + loadModelFromFile(Filename, fboFormat); + } +} + +void ModelViewer::onGuiRender(Gui* pGui) +{ + Gui::Window w(pGui, "Model Viewer", { 400, 300 }, { 0, 100 }); + + // Load model group + if (w.button("Load Model")) + { + loadModel(gpFramework->getTargetFbo()->getColorTexture(0)->getFormat()); + } + + { + auto loadGroup = w.group("Load Options"); + loadGroup.checkbox("Use Original Tangents", mUseOriginalTangents); + loadGroup.tooltip("If this is unchecked, we will ignore the tangents that were loaded from the model and calculate them internally. Check this box if you'd like to use the original tangents"); + loadGroup.checkbox("Remove Duplicate Materials", mRemoveDuplicateMaterials); + loadGroup.tooltip("Deduplicate materials that have the same properties. The material name is ignored during the search"); + } + + w.separator(); + w.checkbox("Wireframe", mDrawWireframe); + + if(mDrawWireframe == false) + { + w.checkbox("Override Rasterizer State", mOverrideRS); + + if(mOverrideRS) + { + Gui::DropdownList cullList; + cullList.push_back({ 0, "No Culling" }); + cullList.push_back({ 1, "Backface Culling" }); + cullList.push_back({ 2, "Frontface Culling" }); + w.dropdown("Cull Mode", cullList, mCullMode); + } + } + + Gui::DropdownList cameraDropdown; + cameraDropdown.push_back({ (uint32_t)Scene::CameraControllerType::FirstPerson, "First-Person" }); + cameraDropdown.push_back({ (uint32_t)Scene::CameraControllerType::Orbiter, "Orbiter" }); + cameraDropdown.push_back({ (uint32_t)Scene::CameraControllerType::SixDOF, "6-DoF" }); + + if (w.dropdown("Camera Type", cameraDropdown, (uint32_t&)mCameraType)) setCamController(); + if (mpScene) mpScene->renderUI(w); +} + +void ModelViewer::onLoad(RenderContext* pRenderContext) +{ + mpProgram = GraphicsProgram::createFromFile("ModelViewer.ps.hlsl", "", "main"); + mpGraphicsState = GraphicsState::create(); + mpGraphicsState->setProgram(mpProgram); + + // create rasterizer state + RasterizerState::Desc wireframeDesc; + wireframeDesc.setFillMode(RasterizerState::FillMode::Wireframe); + wireframeDesc.setCullMode(RasterizerState::CullMode::None); + mpWireframeRS = RasterizerState::create(wireframeDesc); + + RasterizerState::Desc solidDesc; + solidDesc.setCullMode(RasterizerState::CullMode::None); + mpCullRastState[0] = RasterizerState::create(solidDesc); + solidDesc.setCullMode(RasterizerState::CullMode::Back); + mpCullRastState[1] = RasterizerState::create(solidDesc); + solidDesc.setCullMode(RasterizerState::CullMode::Front); + mpCullRastState[2] = RasterizerState::create(solidDesc); + + // Depth test + DepthStencilState::Desc dsDesc; + dsDesc.setDepthEnabled(false); + mpNoDepthDS = DepthStencilState::create(dsDesc); + dsDesc.setDepthFunc(ComparisonFunc::Less).setDepthEnabled(true); + mpDepthTestDS = DepthStencilState::create(dsDesc); + + Sampler::Desc samplerDesc; + samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); + mpPointSampler = Sampler::create(samplerDesc); + samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + mpLinearSampler = Sampler::create(samplerDesc); + + resetCamera(); +} + +void ModelViewer::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +{ + const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); + pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); + mpGraphicsState->setFbo(pTargetFbo); + + if(mpScene) + { + mpScene->update(pRenderContext, gpFramework->getGlobalClock().now()); + + // Set render state + Scene::RenderFlags renderFlags = Scene::RenderFlags::None; + if(mDrawWireframe) + { + renderFlags |= Scene::RenderFlags::UserRasterizerState; + mpGraphicsState->setRasterizerState(mpWireframeRS); + mpGraphicsState->setDepthStencilState(mpNoDepthDS); + mpProgramVars["PerFrameCB"]["gConstColor"] = true; + } + else + { + mpProgramVars["PerFrameCB"]["gConstColor"] = false; + mpGraphicsState->setDepthStencilState(mpDepthTestDS); + if (mOverrideRS) + { + renderFlags |= Scene::RenderFlags::UserRasterizerState; + mpGraphicsState->setRasterizerState(mpCullRastState[mCullMode]); + } + } + + mpGraphicsState->setProgram(mpProgram); + mpScene->render(pRenderContext, mpGraphicsState.get(), mpProgramVars.get(), renderFlags); + } + + TextRenderer::render(pRenderContext, mModelString, pTargetFbo, glm::vec2(10, 30)); +} + +bool ModelViewer::onKeyEvent(const KeyboardEvent& keyEvent) +{ + if (mpScene && mpScene->onKeyEvent(keyEvent)) return true; + + if ((keyEvent.type == KeyboardEvent::Type::KeyPressed) && (keyEvent.key == KeyboardEvent::Key::R)) + { + resetCamera(); + return true; + } + return false; +} + +bool ModelViewer::onMouseEvent(const MouseEvent& mouseEvent) +{ + return mpScene ? mpScene->onMouseEvent(mouseEvent) : false; +} + +void ModelViewer::onResizeSwapChain(uint32_t width, uint32_t height) +{ + float h = (float)height; + float w = (float)width; +} + +void ModelViewer::setCamController() +{ + if(mpScene) mpScene->setCameraController(mCameraType); +} + +void ModelViewer::resetCamera() +{ + if(mpScene) + { + mpScene->resetCamera(true); + setCamController(); + } +} + +#ifdef _WIN32 +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) +#else +int main(int argc, char** argv) +#endif +{ + ModelViewer::UniquePtr pRenderer = std::make_unique(); + + SampleConfig config; + config.windowDesc.title = "Falcor Model Viewer"; + config.windowDesc.resizableWindow = true; +#ifdef _WIN32 + Sample::run(config, pRenderer); +#else + config.argc = (uint32_t)argc; + config.argv = argv; + Sample::run(config, pRenderer); +#endif + return 0; +} diff --git a/Samples/Utils/ModelViewer/ModelViewer.h b/Source/Samples/ModelViewer/ModelViewer.h similarity index 60% rename from Samples/Utils/ModelViewer/ModelViewer.h rename to Source/Samples/ModelViewer/ModelViewer.h index 27a4b4a84..1f05aa015 100644 --- a/Samples/Utils/ModelViewer/ModelViewer.h +++ b/Source/Samples/ModelViewer/ModelViewer.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,34 +27,25 @@ ***************************************************************************/ #pragma once #include "Falcor.h" -#include "Utils/Picking/Picking.h" using namespace Falcor; -class ModelViewer : public Renderer +class ModelViewer : public IRenderer { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onResizeSwapChain(uint32_t width, uint32_t height) override; + bool onKeyEvent(const KeyboardEvent& keyEvent) override; + bool onMouseEvent(const MouseEvent& mouseEvent) override; + void onGuiRender(Gui* pGui) override; private: void loadModel(ResourceFormat fboFormat); - void saveModel(); - void deleteCulledMeshes(); - void loadModelFromFile(const std::string& Filename, ResourceFormat fboFormat); void resetCamera(); - void renderModelUI(Gui* pGui); - void setModelString(bool isAfterCull, float LoadTime); - - Model::SharedPtr mpModel = nullptr; - ModelViewCameraController mModelViewCameraController; - FirstPersonCameraController mFirstPersonCameraController; - SixDoFCameraController m6DoFCameraController; + void setModelString(double loadTime); + void setCamController(); bool mUseTriLinearFiltering = true; Sampler::SharedPtr mpPointSampler = nullptr; @@ -64,25 +55,14 @@ class ModelViewer : public Renderer GraphicsVars::SharedPtr mpProgramVars = nullptr; GraphicsState::SharedPtr mpGraphicsState = nullptr; - enum - { - ModelViewCamera, - FirstPersonCamera, - SixDoFCamera - } mCameraType = ModelViewCamera; - - CameraController& getActiveCameraController(); - - Camera::SharedPtr mpCamera; - bool mDrawWireframe = false; - bool mAnimate = false; - bool mGenerateTangentSpace = true; - glm::vec3 mAmbientIntensity = glm::vec3(0.1f, 0.1f, 0.1f); + bool mUseOriginalTangents = false; + bool mRemoveDuplicateMaterials = true; + Scene::CameraControllerType mCameraType = Scene::CameraControllerType::FirstPerson; - uint32_t mActiveAnimationID = kBindPoseAnimationID; - static const uint32_t kBindPoseAnimationID = AnimationController::kBindPoseAnimationId; + Scene::SharedPtr mpScene; + bool mOverrideRS = false; RasterizerState::SharedPtr mpWireframeRS = nullptr; RasterizerState::SharedPtr mpCullRastState[3]; // 0 = no culling, 1 = backface culling, 2 = frontface culling uint32_t mCullMode = 1; @@ -90,12 +70,5 @@ class ModelViewer : public Renderer DepthStencilState::SharedPtr mpNoDepthDS = nullptr; DepthStencilState::SharedPtr mpDepthTestDS = nullptr; - DirectionalLight::SharedPtr mpDirLight; - PointLight::SharedPtr mpPointLight; - std::string mModelString; - static const std::string skDefaultModel; - - float mNearZ; - float mFarZ; }; diff --git a/Samples/Utils/ModelViewer/ModelViewer.vcxproj b/Source/Samples/ModelViewer/ModelViewer.vcxproj similarity index 90% rename from Samples/Utils/ModelViewer/ModelViewer.vcxproj rename to Source/Samples/ModelViewer/ModelViewer.vcxproj index df1b1eff1..38a63bc07 100644 --- a/Samples/Utils/ModelViewer/ModelViewer.vcxproj +++ b/Source/Samples/ModelViewer/ModelViewer.vcxproj @@ -16,17 +16,17 @@ - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - true true + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + {7BFFD891-AAD6-4E5C-8ADC-611C2625DCD9} Win32Proj @@ -51,10 +51,10 @@ - + - + @@ -70,6 +70,8 @@ Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows @@ -85,6 +87,8 @@ true true WIN32;NDEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows diff --git a/Samples/Utils/ModelViewer/ModelViewer.vcxproj.filters b/Source/Samples/ModelViewer/ModelViewer.vcxproj.filters similarity index 100% rename from Samples/Utils/ModelViewer/ModelViewer.vcxproj.filters rename to Source/Samples/ModelViewer/ModelViewer.vcxproj.filters diff --git a/Samples/Core/ProjectTemplate/ProjectTemplate.cpp b/Source/Samples/ProjectTemplate/ProjectTemplate.cpp similarity index 73% rename from Samples/Core/ProjectTemplate/ProjectTemplate.cpp rename to Source/Samples/ProjectTemplate/ProjectTemplate.cpp index 3b048ea28..7b4c0669e 100644 --- a/Samples/Core/ProjectTemplate/ProjectTemplate.cpp +++ b/Source/Samples/ProjectTemplate/ProjectTemplate.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,46 +26,52 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include "ProjectTemplate.h" +uint32_t mSampleGuiWidth = 250; +uint32_t mSampleGuiHeight = 200; +uint32_t mSampleGuiPositionX = 20; +uint32_t mSampleGuiPositionY = 40; -void ProjectTemplate::onGuiRender(SampleCallbacks* pSample, Gui* pGui) +void ProjectTemplate::onGuiRender(Gui* pGui) { - pGui->addText("Hello from ProjectTemplate"); - if (pGui->addButton("Click Here")) + Gui::Window w(pGui, "Falcor", { 250, 200 }); + gpFramework->renderGlobalUI(pGui); + w.text("Hello from ProjectTemplate"); + if (w.button("Click Here")) { msgBox("Now why would you do that?"); } } -void ProjectTemplate::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) +void ProjectTemplate::onLoad(RenderContext* pRenderContext) { } -void ProjectTemplate::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void ProjectTemplate::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) { const glm::vec4 clearColor(0.38f, 0.52f, 0.10f, 1); pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); } -void ProjectTemplate::onShutdown(SampleCallbacks* pSample) +void ProjectTemplate::onShutdown() { } -bool ProjectTemplate::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) +bool ProjectTemplate::onKeyEvent(const KeyboardEvent& keyEvent) { return false; } -bool ProjectTemplate::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) +bool ProjectTemplate::onMouseEvent(const MouseEvent& mouseEvent) { return false; } -void ProjectTemplate::onDataReload(SampleCallbacks* pSample) +void ProjectTemplate::onDataReload() { } -void ProjectTemplate::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) +void ProjectTemplate::onResizeSwapChain(uint32_t width, uint32_t height) { } diff --git a/Source/Samples/ProjectTemplate/ProjectTemplate.h b/Source/Samples/ProjectTemplate/ProjectTemplate.h new file mode 100644 index 000000000..d59f799ce --- /dev/null +++ b/Source/Samples/ProjectTemplate/ProjectTemplate.h @@ -0,0 +1,46 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once +#include "Falcor.h" + +using namespace Falcor; + +class ProjectTemplate : public IRenderer +{ +public: + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onShutdown() override; + void onResizeSwapChain(uint32_t width, uint32_t height) override; + bool onKeyEvent(const KeyboardEvent& keyEvent) override; + bool onMouseEvent(const MouseEvent& mouseEvent) override; + void onDataReload() override; + void onGuiRender(Gui* pGui) override; + +private: +}; diff --git a/Samples/Core/ProjectTemplate/ProjectTemplate.vcxproj b/Source/Samples/ProjectTemplate/ProjectTemplate.vcxproj similarity index 89% rename from Samples/Core/ProjectTemplate/ProjectTemplate.vcxproj rename to Source/Samples/ProjectTemplate/ProjectTemplate.vcxproj index be9b5b680..7cf9c21f3 100644 --- a/Samples/Core/ProjectTemplate/ProjectTemplate.vcxproj +++ b/Source/Samples/ProjectTemplate/ProjectTemplate.vcxproj @@ -17,8 +17,8 @@ - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} @@ -45,10 +45,10 @@ - + - + @@ -64,6 +64,8 @@ Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows @@ -79,6 +81,8 @@ true true WIN32;NDEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows diff --git a/Samples/Core/ProjectTemplate/ProjectTemplate.vcxproj.filters b/Source/Samples/ProjectTemplate/ProjectTemplate.vcxproj.filters similarity index 100% rename from Samples/Core/ProjectTemplate/ProjectTemplate.vcxproj.filters rename to Source/Samples/ProjectTemplate/ProjectTemplate.vcxproj.filters diff --git a/Samples/Core/ShaderToy/Data/toy.hlsl b/Source/Samples/ShaderToy/Data/toy.hlsl similarity index 98% rename from Samples/Core/ShaderToy/Data/toy.hlsl rename to Source/Samples/ShaderToy/Data/toy.hlsl index ad5eafa84..307f7688b 100644 --- a/Samples/Core/ShaderToy/Data/toy.hlsl +++ b/Source/Samples/ShaderToy/Data/toy.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Samples/Core/ShaderToy/Data/toyContainer.hlsl b/Source/Samples/ShaderToy/Data/toyContainer.hlsl similarity index 97% rename from Samples/Core/ShaderToy/Data/toyContainer.hlsl rename to Source/Samples/ShaderToy/Data/toyContainer.hlsl index bb2138406..cc36160c5 100644 --- a/Samples/Core/ShaderToy/Data/toyContainer.hlsl +++ b/Source/Samples/ShaderToy/Data/toyContainer.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Samples/Core/ShaderToy/ShaderToy.cpp b/Source/Samples/ShaderToy/ShaderToy.cpp similarity index 72% rename from Samples/Core/ShaderToy/ShaderToy.cpp rename to Source/Samples/ShaderToy/ShaderToy.cpp index f3dfa770e..87e8f63a3 100644 --- a/Samples/Core/ShaderToy/ShaderToy.cpp +++ b/Source/Samples/ShaderToy/ShaderToy.cpp @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ ShaderToy::~ShaderToy() { } -void ShaderToy::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) +void ShaderToy::onLoad(RenderContext* pRenderContext) { // create rasterizer state RasterizerState::Desc rsDesc; @@ -39,8 +39,8 @@ void ShaderToy::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) // Depth test DepthStencilState::Desc dsDesc; - dsDesc.setDepthTest(false); - mpNoDepthDS = DepthStencilState::create(dsDesc); + dsDesc.setDepthEnabled(false); + mpNoDepthDS = DepthStencilState::create(dsDesc); // Blend state BlendState::Desc blendDesc; @@ -53,36 +53,25 @@ void ShaderToy::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) // Load shaders mpMainPass = FullScreenPass::create("toyContainer.hlsl"); - - // Create Constant buffer - mpToyVars = GraphicsVars::create(mpMainPass->getProgram()->getReflector()); - - // Get buffer finding - mToyCBBinding = mpMainPass->getProgram()->getReflector()->getDefaultParameterBlock()->getResourceBinding("ToyCB"); } -void ShaderToy::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void ShaderToy::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) { // iResolution float width = (float)pTargetFbo->getWidth(); float height = (float)pTargetFbo->getHeight(); - ParameterBlock* pDefaultBlock = mpToyVars->getDefaultBlock().get(); - pDefaultBlock->getConstantBuffer(mToyCBBinding, 0)["iResolution"] = glm::vec2(width, height);; - - // iGlobalTime - float iGlobalTime = (float)pSample->getCurrentTime(); - pDefaultBlock->getConstantBuffer(mToyCBBinding, 0)["iGlobalTime"] = iGlobalTime; + mpMainPass["ToyCB"]["iResolution"] = glm::vec2(width, height); + mpMainPass["ToyCB"]["iGlobalTime"] = (float)gpFramework->getGlobalClock().now(); // run final pass - pRenderContext->setGraphicsVars(mpToyVars); - mpMainPass->execute(pRenderContext); + mpMainPass->execute(pRenderContext, pTargetFbo); } -void ShaderToy::onShutdown(SampleCallbacks* pSample) +void ShaderToy::onShutdown() { } -bool ShaderToy::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) +bool ShaderToy::onKeyEvent(const KeyboardEvent& keyEvent) { bool bHandled = false; { @@ -98,13 +87,13 @@ bool ShaderToy::onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEve return bHandled; } -bool ShaderToy::onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) +bool ShaderToy::onMouseEvent(const MouseEvent& mouseEvent) { bool bHandled = false; return bHandled; } -void ShaderToy::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) +void ShaderToy::onResizeSwapChain(uint32_t width, uint32_t height) { mAspectRatio = (float(width) / float(height)); } diff --git a/Samples/Core/ShaderToy/ShaderToy.h b/Source/Samples/ShaderToy/ShaderToy.h similarity index 71% rename from Samples/Core/ShaderToy/ShaderToy.h rename to Source/Samples/ShaderToy/ShaderToy.h index e6e90e5b4..2532ead30 100644 --- a/Samples/Core/ShaderToy/ShaderToy.h +++ b/Source/Samples/ShaderToy/ShaderToy.h @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,17 +30,17 @@ using namespace Falcor; -class ShaderToy : public Renderer +class ShaderToy : public IRenderer { public: ~ShaderToy(); - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onShutdown(SampleCallbacks* pSample) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override; + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onShutdown() override; + void onResizeSwapChain(uint32_t width, uint32_t height) override; + bool onKeyEvent(const KeyboardEvent& keyEvent) override; + bool onMouseEvent(const MouseEvent& mouseEvent) override; private: Sampler::SharedPtr mpLinearSampler; @@ -48,8 +48,5 @@ class ShaderToy : public Renderer RasterizerState::SharedPtr mpNoCullRastState; DepthStencilState::SharedPtr mpNoDepthDS; BlendState::SharedPtr mpOpaqueBS; - FullScreenPass::UniquePtr mpMainPass; - GraphicsVars::SharedPtr mpToyVars; - ProgramReflection::BindLocation mToyCBBinding; - -}; \ No newline at end of file + FullScreenPass::SharedPtr mpMainPass; +}; diff --git a/Samples/Core/ShaderToy/ShaderToy.vcxproj b/Source/Samples/ShaderToy/ShaderToy.vcxproj similarity index 90% rename from Samples/Core/ShaderToy/ShaderToy.vcxproj rename to Source/Samples/ShaderToy/ShaderToy.vcxproj index 5d558845a..014999dac 100644 --- a/Samples/Core/ShaderToy/ShaderToy.vcxproj +++ b/Source/Samples/ShaderToy/ShaderToy.vcxproj @@ -16,15 +16,15 @@ - - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - - + + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + + {8AB4CF3D-9824-4390-8569-B07776C4D1F6} Win32Proj @@ -50,10 +50,10 @@ - + - + @@ -69,6 +69,8 @@ Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows @@ -84,6 +86,8 @@ true true WIN32;NDEBUG;%(PreprocessorDefinitions) + stdcpp17 + true Windows diff --git a/Samples/Core/ShaderToy/ShaderToy.vcxproj.filters b/Source/Samples/ShaderToy/ShaderToy.vcxproj.filters similarity index 100% rename from Samples/Core/ShaderToy/ShaderToy.vcxproj.filters rename to Source/Samples/ShaderToy/ShaderToy.vcxproj.filters diff --git a/Samples/Core/make_new_project.py b/Source/Samples/make_new_project.py similarity index 91% rename from Samples/Core/make_new_project.py rename to Source/Samples/make_new_project.py index 13952b7e1..97aef0d68 100644 --- a/Samples/Core/make_new_project.py +++ b/Source/Samples/make_new_project.py @@ -4,7 +4,7 @@ import os if len(sys.argv) != 2: - print("Usage: make_new_project ") + print('Usage: make_new_project ') sys.exit(1) # copy the ProjectTemplate directory diff --git a/Samples/Utils/FalcorTest/FalcorTest.cpp b/Source/Tools/FalcorTest/FalcorTest.cpp similarity index 83% rename from Samples/Utils/FalcorTest/FalcorTest.cpp rename to Source/Tools/FalcorTest/FalcorTest.cpp index 1b788d142..8fc2d4c3b 100644 --- a/Samples/Utils/FalcorTest/FalcorTest.cpp +++ b/Source/Tools/FalcorTest/FalcorTest.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include "FalcorTest.h" - -#include "UnitTest.h" +#include "Testing/UnitTest.h" #include #include #include @@ -36,7 +35,7 @@ static std::vector librariesWithTests = { }; -void FalcorTest::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) +void FalcorTest::onLoad(RenderContext* pRenderContext) { // Load all the DLLs so that they can register their tests. for (const auto& lib : librariesWithTests) @@ -45,10 +44,10 @@ void FalcorTest::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) } } -void FalcorTest::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void FalcorTest::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) { const char* kTestFilterSwitch = "test_filter"; - ArgList argList = pSample->getArgList(); + ArgList argList = gpFramework->getArgList(); std::string testFilterRegex; if (argList.argExists(kTestFilterSwitch)) { @@ -58,13 +57,13 @@ void FalcorTest::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderC if (argList.argExists("h") || argList.argExists("help")) { fprintf(stderr, R"(usage: FalcorTest [-test_filter filter] -Where, if |filter| is provided, only tests whose source filename or test name -have |filter| as a substring are executed. -)"); + Where, if |filter| is provided, only tests whose source filename or test name + have |filter| as a substring are executed. + )"); } runTests(stderr, pRenderContext, testFilterRegex); - pSample->shutdown(); + gpFramework->shutdown(); } int main(int argc, char** argv) @@ -73,7 +72,7 @@ int main(int argc, char** argv) SampleConfig config; config.windowDesc.title = "FalcorTest"; config.windowDesc.resizableWindow = true; - config.argc = argc; - config.argv = argv; - Sample::run(config, pRenderer); + config.windowDesc.width = config.windowDesc.height = 2; + Sample::run(config, pRenderer, argc, argv); + return 0; } diff --git a/Samples/Utils/FalcorTest/FalcorTest.h b/Source/Tools/FalcorTest/FalcorTest.h similarity index 88% rename from Samples/Utils/FalcorTest/FalcorTest.h rename to Source/Tools/FalcorTest/FalcorTest.h index 5d309242b..cdb2f78f0 100644 --- a/Samples/Utils/FalcorTest/FalcorTest.h +++ b/Source/Tools/FalcorTest/FalcorTest.h @@ -31,9 +31,9 @@ using namespace Falcor; -class FalcorTest : public Renderer +class FalcorTest : public IRenderer { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; }; diff --git a/Samples/Utils/FalcorTest/FalcorTest.vcxproj b/Source/Tools/FalcorTest/FalcorTest.vcxproj similarity index 59% rename from Samples/Utils/FalcorTest/FalcorTest.vcxproj rename to Source/Tools/FalcorTest/FalcorTest.vcxproj index 1427ca588..dd3d256e1 100644 --- a/Samples/Utils/FalcorTest/FalcorTest.vcxproj +++ b/Source/Tools/FalcorTest/FalcorTest.vcxproj @@ -1,4 +1,4 @@ - + @@ -12,18 +12,46 @@ - + + + + + + + + + + + + + + + + + + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} - + + + + + + + + + + + + - + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} + {20401FAD-6022-8EB7-2F78-41369B8F0F49} @@ -50,10 +78,10 @@ - + - + @@ -69,6 +97,9 @@ Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions) + true + stdcpp17 + true Console @@ -84,6 +115,9 @@ true true WIN32;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp17 + true Console diff --git a/Source/Tools/FalcorTest/FalcorTest.vcxproj.filters b/Source/Tools/FalcorTest/FalcorTest.vcxproj.filters new file mode 100644 index 000000000..d2d194199 --- /dev/null +++ b/Source/Tools/FalcorTest/FalcorTest.vcxproj.filters @@ -0,0 +1,127 @@ + + + + + + Tests\Utils + + + Tests\Utils + + + Tests\Utils + + + Tests\Slang + + + Tests\ShadingUtils + + + Tests\Utils + + + Tests\Utils + + + Tests\Utils + + + Tests\Sampling + + + Tests\Sampling + + + Tests\Scene + + + Tests\Utils + + + Tests\Utils + + + Tests\DebugPasses + + + Tests\Utils + + + Tests\ShadingUtils + + + Tests\Core + + + Tests\Utils + + + + + + + + {ce64d89a-ce01-4012-9706-d3f24f5da801} + + + {0d6b912d-7c18-415e-af37-399e137194d5} + + + {619ed051-9ecd-4324-8b5a-bb65a8b608ed} + + + {68dbdd58-1038-443a-a47c-4ba6afed1f99} + + + {b68d5b46-cd0d-4739-99d0-645cc1d11377} + + + {d5138b9b-d23f-415e-af68-eafc7cbdfb91} + + + {7c5c7694-8d37-40c3-9f3d-48a95c0e9d80} + + + {ae20200a-382a-40ce-a8ab-40af7c9a512c} + + + + + Tests\ShadingUtils + + + Tests\Slang + + + Tests\Slang + + + Tests\Utils + + + Tests\Sampling + + + Tests\Sampling + + + Tests\Utils + + + Tests\Utils + + + Tests\Utils + + + Tests\ShadingUtils + + + Tests\Core + + + Tests\Utils + + + \ No newline at end of file diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp new file mode 100644 index 000000000..183ceee98 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" + +namespace Falcor +{ + namespace + { + const uint32_t kIterations = 4; + + enum class Type + { + ByteAddressBuffer = 0, + TypedBuffer = 1, + StructuredBuffer = 2, + }; + + template + void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, uint32_t count = 0) + { + numElems = div_round_up(numElems, 256u) * 256u; // Make sure we run full thread groups. + + // Create a data blob for the test. + // We fill it some numbers that don't overlap with the test buffers values. + std::vector blob; + if (count > 0) + { + blob.resize(count); + for (uint32_t i = 0; i < blob.size(); i++) blob[i] = (count - i) * 3; + } + + // Create program and test buffer. + Program::DefineList defines = { { "TYPE", std::to_string((uint32_t)type) } }; + ctx.createProgram("Tests/Core/BufferTests.cs.slang", "clearBuffer", defines); + + typename BufferT::SharedPtr pBuffer; + if constexpr (type == Type::ByteAddressBuffer) pBuffer = BufferT::create(numElems * sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None); + else if constexpr (type == Type::TypedBuffer) pBuffer = BufferT::create(numElems, ResourceBindFlags::UnorderedAccess); + else if constexpr (type == Type::StructuredBuffer) pBuffer = StructuredBuffer::create(ctx.getProgram(), "buffer", numElems, ResourceBindFlags::UnorderedAccess); + else static_assert(false); + + if constexpr (type == Type::ByteAddressBuffer) ctx.vars().setRawBuffer("buffer", pBuffer); + else if constexpr (type == Type::TypedBuffer) ctx.vars().setTypedBuffer("buffer", pBuffer); + else if constexpr (type == Type::StructuredBuffer) ctx.vars().setStructuredBuffer("buffer", pBuffer); + else static_assert(false); + + // Run kernel to clear the buffer. + // We clear explicitly instead of using clearUAV() as the latter is not compatible with RWStructuredBuffer. + ctx.runProgram(numElems, 1, 1); + + ctx.createProgram("Tests/Core/BufferTests.cs.slang", "updateBuffer", defines); + + if constexpr (type == Type::ByteAddressBuffer) ctx.vars().setRawBuffer("buffer", pBuffer); + else if constexpr (type == Type::TypedBuffer) ctx.vars().setTypedBuffer("buffer", pBuffer); + else if constexpr (type == Type::StructuredBuffer) ctx.vars().setStructuredBuffer("buffer", pBuffer); + else static_assert(false); + + // Run kernel N times to update the buffer (RMW). + for (uint32_t i = 0; i < kIterations; i++) ctx.runProgram(numElems, 1, 1); + + // Use setBlob() to update part of the buffer from the CPU. + if (count > 0) + { + assert(index + blob.size() <= numElems); + pBuffer->setBlob(blob.data(), index * sizeof(uint32_t), blob.size() * sizeof(uint32_t)); + } + + // Run kernel N times to update the buffer (RMW). + for (uint32_t i = 0; i < kIterations; i++) ctx.runProgram(numElems, 1, 1); + + // Run kernel to read values in the buffer. + ctx.createProgram("Tests/Core/BufferTests.cs.slang", "readBuffer", defines); + ctx.allocateStructuredBuffer("result", numElems); + + if constexpr (type == Type::ByteAddressBuffer) ctx.vars().setRawBuffer("buffer", pBuffer); + else if constexpr (type == Type::TypedBuffer) ctx.vars().setTypedBuffer("buffer", pBuffer); + else if constexpr (type == Type::StructuredBuffer) ctx.vars().setStructuredBuffer("buffer", pBuffer); + else static_assert(false); + + ctx.runProgram(numElems, 1, 1); + + // Verify results. + const uint32_t* pResult = ctx.mapBuffer("result"); + for (uint32_t i = 0; i < numElems; i++) + { + // Each RMW pass adds i+1 to the element at index i. + // We run kIterations passes, then replace part of the buffer, followed by kIterations more passes. + uint32_t expected = (i + 1) * kIterations * 2; + if (i >= index && i < index + blob.size()) expected = blob[i - index] + (i + 1) * kIterations; + uint32_t result = pResult[i]; + + EXPECT_EQ(result, expected) << "i = " << i << " (numElems = " << numElems << " index = " << index << " count = " << count << ")"; + } + ctx.unmapBuffer("result"); + } + } + + GPU_TEST(RawBuffer) + { + auto testFunc = testBuffer; + for (uint32_t numElems = 1u << 8; numElems <= (1u << 20); numElems <<= 4) + { + testFunc(ctx, numElems, 0, 0); + testFunc(ctx, numElems, 0, 1); + testFunc(ctx, numElems, 0, numElems / 2); + testFunc(ctx, numElems, 1, 1); + testFunc(ctx, numElems, numElems / 2 + 3, numElems / 4 - 1); + } + } + +#if 0 + // Tests disables as setBlob() on TypedBuffer and StructuredBuffer doesn't work the same way as on Buffer. + // For TypedBuffer and StructuredBuffer, the CPU copy of the entire buffer is re-uploaded to the GPU, + // which invalidates previous data we may have populated them with on the GPU. + // The Buffer class doesn't keep an internal CPU copy, so it won't invalidate the contents. + + GPU_TEST(TypedBuffer) + { + auto testFunc = testBuffer>; + for (uint32_t numElems = 1u << 8; numElems <= (1u << 20); numElems <<= 4) + { + testFunc(ctx, numElems, 0, 0); + testFunc(ctx, numElems, 0, 1); + testFunc(ctx, numElems, 0, numElems / 2); + testFunc(ctx, numElems, 1, 1); + testFunc(ctx, numElems, numElems / 2 + 3, numElems / 4 - 1); + } + } + + GPU_TEST(StructuredBuffer) + { + auto testFunc = testBuffer; + for (uint32_t numElems = 1u << 8; numElems <= (1u << 20); numElems <<= 4) + { + testFunc(ctx, numElems, 0, 0); + testFunc(ctx, numElems, 0, 1); + testFunc(ctx, numElems, 0, numElems / 2); + testFunc(ctx, numElems, 1, 1); + testFunc(ctx, numElems, numElems / 2 + 3, numElems / 4 - 1); + } + } + +#endif +} diff --git a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cs.slang similarity index 58% rename from Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.cpp rename to Source/Tools/FalcorTest/Tests/Core/BufferTests.cs.slang index d8cd635e8..125dc77a8 100644 --- a/Samples/Core/LearningWithEmbeddedPython/LearningWithEmbeddedPython.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,40 +26,55 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#include "Utils/Renderer/MultiSampleRenderer.h" -#include "Renderers/LiveTrainingDemo.h" +/** Unit tests for RMW operations on different buffer types. -#ifdef _WIN32 -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) -#else -int main(int argc, char** argv) + The updateBuffer() kernel is run multiple times followed by readBuffer() + that copies the result to the output buffer. +*/ + +#if TYPE == 0 +RWByteAddressBuffer buffer; +#elif TYPE == 1 +RWBuffer buffer; +#elif TYPE == 2 +RWStructuredBuffer buffer; #endif -{ -#if FALCOR_USE_PYTHON - // Setup our demo config - SampleConfig config; - config.windowDesc.title = "Live Training & Python Integration Demo"; - config.windowDesc.resizableWindow = false; - config.windowDesc.width = 1800; - config.windowDesc.height = 980; - config.freezeTimeOnStartup = true; - // Create our Python integration renderer - Renderer::UniquePtr pLiveTrain = std::make_unique(); +RWStructuredBuffer result; - // Run the program -#ifdef _WIN32 - Sample::run(config, pLiveTrain); +// Clear kernel. +// This is necessary for RWStructuredBuffer as we can't use ID3D12CommandList::ClearUnorderedAccessViewUint for structured buffers. +[numthreads(256, 1, 1)] +void clearBuffer(uint3 threadId : SV_DispatchThreadID) +{ + uint i = threadId.x; +#if TYPE == 0 + buffer.Store(i * 4, 0); #else - config.argc = (uint32_t)argc; - config.argv = argv; - Sample::run(config, pRenderer); + buffer[i] = 0; #endif - return 0; -#else - logWarning("Python is not enabled! Please set FALCOR_USE_PYTHON to 1 in FalcorConfig.h \ - Read README.txt in the LearningWithEmbeddedPython directory to ensure correct version of packages.", true); - return 1; +} + +[numthreads(256, 1, 1)] +void updateBuffer(uint3 threadId : SV_DispatchThreadID) +{ + uint i = threadId.x; +#if TYPE == 0 + uint val = buffer.Load(i * 4); + val += i + 1; + buffer.Store(i * 4, val); +#else + buffer[i] += i + 1; #endif } +[numthreads(256, 1, 1)] +void readBuffer(uint3 threadId : SV_DispatchThreadID) +{ + uint i = threadId.x; +#if TYPE == 0 + result[i] = buffer.Load(i * 4); +#else + result[i] = buffer[i]; +#endif +} diff --git a/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp new file mode 100644 index 000000000..acc44e6b4 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" + +namespace Falcor +{ + GPU_TEST(InvalidPixelDetectionPass) + { + RenderPassLibrary::instance().loadLibrary("DebugPasses.dll"); + float pInitData[8] = {std::numeric_limits::quiet_NaN(), std::numeric_limits::signaling_NaN(), std::numeric_limits::infinity(), + -1 * std::numeric_limits::infinity(), 0.0f, 255.0f, 125.8f, 1.0f}; + RenderContext* pRenderContext = ctx.getRenderContext(); + Texture::SharedPtr pInput = Texture::create2D(2, 4, ResourceFormat::R32Float, 1, Resource::kMaxPossible, pInitData); + RenderGraph::SharedPtr pGraph = RenderGraph::create("Invalid Pixel Detection"); + RenderPass::SharedPtr pPass = RenderPassLibrary::instance().createPass(pRenderContext, "InvalidPixelDetectionPass"); + pGraph->addPass(pPass, "InvalidPixelDetectionPass"); + pGraph->setInput("InvalidPixelDetectionPass.src", pInput); + pGraph->markOutput("InvalidPixelDetectionPass.dst"); + pGraph->execute(pRenderContext); + Resource::SharedPtr pOutput = pGraph->getOutput("InvalidPixelDetectionPass.dst"); + std::vector color = pRenderContext->readTextureSubresource(pOutput->asTexture().get(), 0); + uint32* output = (uint32_t*)color.data(); + + for (uint32_t i = 0; i < 8; ++i) + { + uint32_t expected; + switch (i) + { + case 0: + case 1: + expected = 0xFFFF0000; + break; + case 2: + case 3: + expected = 0xFF00FF00; + break; + default: + expected = 0xFF000000; + break; + } + EXPECT_EQ(output[i], expected); + } + } +} diff --git a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp new file mode 100644 index 000000000..7995fe456 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include + +/** GPU tests for the various pseusorandom number generators. + + We create a number of parallel instances of the PRNGs, seeded randomly, + and let each instance create a sequence of random numbers up to a certain + dimension. The result is compared against CPU reference implementations. +*/ + +namespace Falcor +{ + // Reference implementation of the xoshiro128** algorithm. + namespace xoshiro128starstar + { + // This sources is unmodified from the author's website. + // We import it into a namespace to be able to use it from the test function. + #include "xoshiro/xoshiro128starstar.c" + } + + // Reference implementation of the SplitMix64 algorithm. + namespace splitmix64 + { + // This sources is unmodified from the author's website. + // We import it into a namespace to be able to use it from the test function. + #include "xoshiro/splitmix64.c" + } + + // Reference implementation of the LCG from Numerical Recipes. + // See https://en.wikipedia.org/wiki/Linear_congruential_generator + namespace lcg + { + static uint32_t state; + uint32_t next() + { + const uint32_t a = 1664525; + const uint32_t c = 1013904223; + state = a * state + c; + return state; + } + } + + // Shared test utils. + namespace + { + const char kShaderFile[] = "Tests/Sampling/PseudorandomTests.cs.slang"; + + const uint32_t kInstances = 256; + const uint32_t kDimensions = 64; + + Buffer::SharedPtr createSeed(size_t elements, std::vector& seed) + { + // Initialize buffer of random seed data. + seed.resize(elements); + std::mt19937 rng; + for (auto& it : seed) it = rng(); + + // Upload seeds to the GPU. + Buffer::SharedPtr pSeedBuf = Buffer::create(seed.size() * sizeof(seed[0]), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, seed.data()); + assert(pSeedBuf); + return pSeedBuf; + } + } + + /** GPU test for Xoshiro pseudorandom number generator. + */ + GPU_TEST(XoshiroPRNG) + { + // Create random seed (128 bits per instance). + std::vector seed; + auto pSeedBuf = createSeed(kInstances * 4, seed); + + // Setup and run GPU test. + ctx.createProgram(kShaderFile, "testXoshiro"); + ctx.allocateStructuredBuffer("result", kInstances * kDimensions); + ctx.vars().setRawBuffer("seed", pSeedBuf); + ctx.runProgram(kInstances); + + // Compare result against reference implementation. + const uint32_t* result = ctx.mapBuffer("result"); + for (uint32_t i = 0; i < kInstances; i++) + { + // Set seed. + xoshiro128starstar::s[0] = seed[i * 4 + 0]; + xoshiro128starstar::s[1] = seed[i * 4 + 1]; + xoshiro128starstar::s[2] = seed[i * 4 + 2]; + xoshiro128starstar::s[3] = seed[i * 4 + 3]; + + for (uint32_t j = 0; j < kDimensions; j++) + { + const uint32_t ref = xoshiro128starstar::next(); + const uint32_t res = result[j * kInstances + i]; + EXPECT_EQ(ref, res) << "instance = " << i << " dimension = " << j; + } + } + ctx.unmapBuffer("result"); + } + + /** GPU test for SplitMix64 pseudorandom number generator. + */ + GPU_TEST(SplitMixPRNG) + { + // Create random seed (64 bits per instance). + std::vector seed; + auto pSeedBuf = createSeed(kInstances * 2, seed); + + // Setup and run GPU test. Note it requires SM 6.0 or higher. + ctx.createProgram(kShaderFile, "testSplitMix", Program::DefineList(), Shader::CompilerFlags::None, "6_0"); + ctx.allocateStructuredBuffer("result64", kInstances * kDimensions); + ctx.vars().setRawBuffer("seed", pSeedBuf); + ctx.runProgram(kInstances); + + // Compare result against reference implementation. + const uint64_t* result = ctx.mapBuffer("result64"); + for (uint32_t i = 0; i < kInstances; i++) + { + // Set seed. + splitmix64::x = (uint64_t(seed[i * 2 + 1]) << 32) | uint64_t(seed[i * 2 + 0]); + + for (uint32_t j = 0; j < kDimensions; j++) + { + const uint64_t ref = splitmix64::next(); + const uint64_t res = result[j * kInstances + i]; + EXPECT_EQ(ref, res) << "instance = " << i << " dimension = " << j; + } + } + ctx.unmapBuffer("result64"); + } + + /** GPU test for LCG pseudorandom number generator. + */ + GPU_TEST(LCGPRNG) + { + // Create random seed (32 bits per instance). + std::vector seed; + auto pSeedBuf = createSeed(kInstances, seed); + + // Setup and run GPU test. + ctx.createProgram(kShaderFile, "testLCG"); + ctx.allocateStructuredBuffer("result", kInstances * kDimensions); + ctx.vars().setRawBuffer("seed", pSeedBuf); + ctx.runProgram(kInstances); + + // Compare result against reference implementation. + const uint32_t* result = ctx.mapBuffer("result"); + for (uint32_t i = 0; i < kInstances; i++) + { + // Set seed. + lcg::state = seed[i]; + + for (uint32_t j = 0; j < kDimensions; j++) + { + const uint32_t ref = lcg::next(); + const uint32_t res = result[j * kInstances + i]; + EXPECT_EQ(ref, res) << "instance = " << i << " dimension = " << j; + } + } + ctx.unmapBuffer("result"); + } +} diff --git a/Samples/Raytracing/PathTracer/Data/GGXGIIndirectRay.slang b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cs.slang similarity index 50% rename from Samples/Raytracing/PathTracer/Data/GGXGIIndirectRay.slang rename to Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cs.slang index f28445201..b8aba5a57 100644 --- a/Samples/Raytracing/PathTracer/Data/GGXGIIndirectRay.slang +++ b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cs.slang @@ -25,43 +25,65 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -import ShaderCommon; -import Raytracing; -import Helpers; -import GGXGICommon; -#include "HostDeviceSharedMacros.h" +import Utils.Sampling.Pseudorandom.Xoshiro; +import Utils.Sampling.Pseudorandom.SplitMix64; +import Utils.Sampling.Pseudorandom.LCG; -[shader("miss")] -void IndirectMiss(inout IndirectRayPayload rayData) +ByteAddressBuffer seed; +RWStructuredBuffer result; // Result buffer for 32-bit generators. +RWStructuredBuffer result64; // Result buffer for 64-bit generators. + +#define kInstances 256 +#define kDimensions 64 + +[numthreads(kInstances, 1, 1)] +void testXoshiro(uint3 threadId : SV_DispatchThreadID) { - // Sample the environment map - float2 uv = dirToSphericalCrd(WorldRayDirection()); - float2 dims; - gEnvMap.GetDimensions(dims.x, dims.y); - rayData.color = float3(gEnvMap[uint2(uv * dims)].rgb); + const uint i = threadId.x; + + // Create pseudorandom number generator. + uint4 state = seed.Load4(i * 16); + const uint s[4] = { state.x, state.y, state.z, state.w }; + Xoshiro128StarStar rng = createXoshiro128StarStar(s); + + // Generate random numbers. + for (uint j = 0; j < kDimensions; j++) + { + uint val = nextRandom(rng); + result[j * kInstances + i] = val; + } } -[shader("anyhit")] -void IndirectAnyHit(inout IndirectRayPayload rayData, BuiltInTriangleIntersectionAttributes attribs) +[numthreads(kInstances, 1, 1)] +void testSplitMix(uint3 threadId : SV_DispatchThreadID) { - if (evalRtAlphaTest(attribs)) - IgnoreHit(); + const uint i = threadId.x; + + // Create pseudorandom number generator. + uint2 state = seed.Load2(i * 8); + SplitMix64 rng = createSplitMix64(state.x, state.y); + + // Generate random numbers. + for (uint j = 0; j < kDimensions; j++) + { + uint64_t val = nextRandom64(rng); + result64[j * kInstances + i] = val; + } } -[shader("closesthit")] -void IndirectClosestHit(inout IndirectRayPayload rayData, BuiltInTriangleIntersectionAttributes attribs) +[numthreads(kInstances, 1, 1)] +void testLCG(uint3 threadId : SV_DispatchThreadID) { - VertexOut vOut = getVertexAttributes(PrimitiveIndex(), attribs); - ShadingData sd = prepareShadingData(vOut, gMaterial, WorldRayOrigin(), 0); - - // Add emissive color - rayData.color = gEmitMult * sd.emissive.rgb; + const uint i = threadId.x; - // Do direct illumination at this hit location - if (gDoDirectGI) rayData.color += ggxDirect(rayData.rndSeed, sd); + // Create pseudorandom number generator. + uint state = seed.Load(i * 4); + LCG rng = createLCG(state); - // Do indirect illumination at this hit location (if we haven't traversed too far) - // Indirect bounces after the first will not check against geometric normals (and therefore possibly leak light) because - // prepareShadingData does not provide that information. This will still produce a good enough result. - if (rayData.rayDepth < gMaxDepth) rayData.color += ggxIndirect(rayData.rndSeed, sd, sd.N, rayData.rayDepth); + // Generate random numbers. + for (uint j = 0; j < kDimensions; j++) + { + uint val = nextRandom(rng); + result[j * kInstances + i] = val; + } } diff --git a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp new file mode 100644 index 000000000..76d4613e5 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Utils/Sampling/SampleGenerator.h" + +/** GPU tests for the SampleGenerator utility class. +*/ + +namespace Falcor +{ + namespace + { + const char kShaderFile[] = "Tests/Sampling/SampleGeneratorTests.cs.slang"; + + // The shader uses the first two dispatch dimensions as spatial seed and the last as instance index. + // For each sample generator instance, it generates kDimensions samples. + const glm::uvec3 kDispatchDim = { 64, 64, 16 }; + const uint32_t kDimensions = 32; + + /** Estimates the population Pearson correlation between pairs of + measurements of a random variable stored in an array 'elems'. + The two values in each pair are separated a distance 'stride'. + The function iterates over all samples i, measuring correlation + between sample i and i+stride, so each value may be part of multiple pairs. + \return Estimated Pearson correlation coefficient in [-1,1], where 0.0 means no correlation. + */ + double correlation(const float* elems, const size_t numElems, const size_t stride) + { + double sum_x = 0.0, sum_y = 0.0; + double sum_xx = 0.0, sum_yy = 0.0, sum_xy = 0.0; + size_t n = 0; + for (size_t i = 0; i + stride < numElems; i++) + { + float x = elems[i]; + float y = elems[i + stride]; + sum_x += x; + sum_y += y; + sum_xx += x * x; + sum_yy += y * y; + sum_xy += x * y; + n++; + } + assert(n > 0); + double r_xy = ((double)n * sum_xy - sum_x * sum_y) / + (sqrt(n * sum_xx - sum_x * sum_x) * sqrt(n * sum_yy - sum_y * sum_y)); + return r_xy; + } + + void testSampleGenerator(GPUUnitTestContext& ctx, uint32_t type, const double corrThreshold, bool testInstances) + { + // Create sample generator. + SampleGenerator::SharedPtr pSampleGenerator = SampleGenerator::create(type); + if (!pSampleGenerator) throw ErrorRunningTestException("Failed to create sample generator of type " + std::to_string(type)); + + // Setup GPU test. + // We defer the creation of the vars until after shader specialization. + ctx.createProgram(kShaderFile, "test", Program::DefineList(), Shader::CompilerFlags::None, "6_0", false); + pSampleGenerator->prepareProgram(ctx.getProgram()); + + ctx.createVars(); + pSampleGenerator->setIntoProgramVars(&ctx.vars()); + + const size_t numSamples = kDispatchDim.x * kDispatchDim.y * kDispatchDim.z * kDimensions; + ctx.allocateStructuredBuffer("result", numSamples); + ctx["CB"]["gDispatchDim"] = kDispatchDim; + ctx["CB"]["gDimensions"] = kDimensions; + + // Run the test. + ctx.runProgram(kDispatchDim); + + // Readback results. + const float* result = ctx.mapBuffer("result"); + + // Check that all samples are in the [0,1) range, + // and that their mean is roughly 0.5. + double mean = 0.0; + for (size_t i = 0; i < numSamples; i++) + { + float u = result[i]; + mean += u; + EXPECT(u >= 0.f && u < 1.f) << u; + } + mean /= numSamples; + EXPECT_GE(mean, 0.499); + EXPECT_LE(mean, 0.501); + + // Check correlation between adjacent samples along different dimensions in the sample set. + // This is not really a robust statistical test, but it should detect if something is fundamentally wrong. + auto corr = [&](size_t stride) -> double + { + return std::abs(correlation(result, numSamples, stride)); + }; + + // Test nearby dimensions. + for (size_t i = 1; i <= 8; i++) + { + EXPECT_LE(corr(i), corrThreshold) << "i = " << i; + } + + // Test nearby pixels. + const size_t xStride = kDimensions; + const size_t yStride = kDispatchDim.x * kDimensions; + for (size_t y = 0; y < 4; y++) + { + for (size_t x = 0; x < 4; x++) + { + if (x == 0 && y == 0) continue; + EXPECT_LE(corr(x * xStride + y * yStride), corrThreshold) << "x = " << x << " y = " << y; + } + } + + // Test nearby instances, if they are expected to be uncorrelated. + if (testInstances) + { + const size_t instanceStride = kDispatchDim.x * kDispatchDim.y * kDimensions; + for (size_t i = 1; i <= 4; i++) + { + EXPECT_LE(corr(i * instanceStride), corrThreshold) << "i = " << i; + } + } + + ctx.unmapBuffer("result"); + } + } + + /** Tests for the different types of sample generators. + + For each one, we specify the maximum allowed absolute correlation between samples. + The values have been tweaked based on observed correlations at these sample counts. + */ + + GPU_TEST(SampleGenerator_TinyUniform) + { + testSampleGenerator(ctx, SAMPLE_GENERATOR_TINY_UNIFORM, 0.0025, true); + } + + GPU_TEST(SampleGenerator_Uniform) + { + testSampleGenerator(ctx, SAMPLE_GENERATOR_UNIFORM, 0.002, true); + } +} diff --git a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cs.slang b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cs.slang new file mode 100644 index 000000000..a0c432f31 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cs.slang @@ -0,0 +1,75 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Utils.Sampling.SampleGenerator; + +cbuffer CB +{ + uint3 gDispatchDim; + uint gDimensions; +} + +RWStructuredBuffer result; + +[numthreads(16, 16, 1)] +void test(uint3 threadId : SV_DispatchThreadID) +{ + if (any(threadId >= gDispatchDim)) return; + + // Create sample generator. + SampleGenerator sg = SampleGenerator.create(threadId.xy, threadId.z); + + // Generate samples. + // The output is a 4D tensor of samples, stored in memory as Z instances + // of 2D tiles XY stored in scanline order, where XYZ is the dispatch dimensions. + // Each element consists of an N-dimensional sample. + const uint pixelIdx = threadId.y * gDispatchDim.x + threadId.x; + const uint tileOffset = threadId.z * (gDispatchDim.x * gDispatchDim.y); + const uint offset = (tileOffset + pixelIdx) * gDimensions; + + for (uint i = 0; i < gDimensions; i += 8) + { + float u0 = sampleNext1D(sg); + float u1 = sampleNext1D(sg); + float u2 = sampleNext1D(sg); + + result[offset + i + 0] = u0; + result[offset + i + 1] = u1; + result[offset + i + 2] = u2; + + float2 v2 = sampleNext2D(sg); + + result[offset + i + 3] = v2.x; + result[offset + i + 4] = v2.y; + + float3 v3 = sampleNext3D(sg); + + result[offset + i + 5] = v3.x; + result[offset + i + 6] = v3.y; + result[offset + i + 7] = v3.z; + } +} diff --git a/Source/Tools/FalcorTest/Tests/Scene/EnvProbeTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/EnvProbeTests.cpp new file mode 100644 index 000000000..6de43f685 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Scene/EnvProbeTests.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Experimental/Scene/Lights/EnvProbe.h" + +namespace Falcor +{ + namespace + { + // This file is located in the Media/ directory fetched by packman. + const char kLightProbeFile[] = "LightProbes/20050806-03_hd.hdr"; + } + + GPU_TEST(EnvProbe) + { + // Test loading a light probe. + // This call runs setup code on the GPU to precompute the importance map. + // If it succeeds, we at least know the code compiles and run. + EnvProbe::SharedPtr pEnvProbe = EnvProbe::create(ctx.getRenderContext(), kLightProbeFile); + EXPECT_NE(pEnvProbe, nullptr); + if (pEnvProbe == nullptr) return; + + // Check that the importance map exists and is a square power-of-two + // texture with a full mip map hierarchy. + auto pImportanceMap = pEnvProbe->getImportanceMap(); + EXPECT_NE(pImportanceMap, nullptr); + if (pImportanceMap == nullptr) return; + + uint32_t w = pImportanceMap->getWidth(); + uint32_t h = pImportanceMap->getHeight(); + uint32_t mipCount = pImportanceMap->getMipCount(); + + EXPECT(isPowerOf2(w) && w > 0); + EXPECT_EQ(w, h); + EXPECT_EQ(w, 1 << (mipCount - 1)); + } +} diff --git a/Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cpp b/Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cpp new file mode 100644 index 000000000..085150b2d --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include +#include + +namespace Falcor +{ + namespace + { + int float_as_int(float f) { return *reinterpret_cast(&f); } + float int_as_float(int i) { return *reinterpret_cast(&i); } + using float3 = glm::vec3; + using int3 = glm::int3; + + /** Unmodified reference code from Ray Tracing Gems, Chapter 6. + */ + constexpr float origin() { return 1.0f / 32.0f; } + constexpr float float_scale() { return 1.0f / 65536.0f; } + constexpr float int_scale() { return 256.0f; } + + // Normal points outward for rays exiting the surface, else is flipped. + float3 offset_ray(const float3 p, const float3 n) + { + int3 of_i(int_scale() * n.x, int_scale() * n.y, int_scale() * n.z); + + float3 p_i( + int_as_float(float_as_int(p.x) + ((p.x < 0) ? -of_i.x : of_i.x)), + int_as_float(float_as_int(p.y) + ((p.y < 0) ? -of_i.y : of_i.y)), + int_as_float(float_as_int(p.z) + ((p.z < 0) ? -of_i.z : of_i.z))); + + return float3(fabsf(p.x) < origin() ? p.x + float_scale()*n.x : p_i.x, + fabsf(p.y) < origin() ? p.y + float_scale()*n.y : p_i.y, + fabsf(p.z) < origin() ? p.z + float_scale()*n.z : p_i.z); + } + } + + GPU_TEST(ComputeRayOrigin) + { + const uint32_t n = 1 << 16; + + std::mt19937 rng; + auto dist = std::uniform_real_distribution(-1.f, 1.f); + auto r = [&]() -> float { return dist(rng); }; + + // Create random test data. + std::vector testPositions(n); + std::vector testNormals(n); + for (uint32_t i = 0; i < n; i++) + { + float scale = std::pow(10.f, (float)i / n * 60.f - 30.f); // 1e-30..1e30 + testPositions[i] = glm::vec3(r(), r(), r()) * scale; + testNormals[i] = glm::normalize(glm::vec3(r(), r(), r())); + } + + // Setup and run GPU test. + ctx.createProgram("Tests/ShadingUtils/RaytracingTests.cs.slang", "testComputeRayOrigin"); + ctx.allocateStructuredBuffer("result", n); + // TODO: Cleanup when !122 is merged + //ctx.allocateStructuredBuffer("pos", n, testPositions, testPositions.size() * sizeof(glm::vec3)); + //ctx.allocateStructuredBuffer("normal", n, testNormals.size() * sizeof(glm::vec3)); + auto pPos = StructuredBuffer::create(ctx.getProgram(), "pos", n); + pPos->setBlob(testPositions.data(), 0, testPositions.size() * sizeof(glm::vec3)); + ctx.vars().setStructuredBuffer("pos", pPos); + auto pNormal = StructuredBuffer::create(ctx.getProgram(), "normal", n); + pNormal->setBlob(testNormals.data(), 0, testNormals.size() * sizeof(glm::vec3)); + ctx.vars().setStructuredBuffer("normal", pNormal); + + ctx["CB"]["n"] = n; + ctx.runProgram(n); + + // Verify results. + const glm::vec3* result = ctx.mapBuffer("result"); + for (uint32_t i = 0; i < n; i++) + { + glm::vec3 ref = offset_ray(testPositions[i], testNormals[i]); + EXPECT_EQ(result[i], ref) << "i = " << i; + } + ctx.unmapBuffer("result"); + } +} diff --git a/Framework/Source/Data/Framework/Shaders/Blit.vs.slang b/Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cs.slang similarity index 80% rename from Framework/Source/Data/Framework/Shaders/Blit.vs.slang rename to Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cs.slang index 3df3f5606..5d1bc66ee 100644 --- a/Framework/Source/Data/Framework/Shaders/Blit.vs.slang +++ b/Source/Tools/FalcorTest/Tests/ShadingUtils/RaytracingTests.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,22 +25,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -cbuffer SrcRectCB : register(b0) -{ - float2 gOffset; - float2 gScale; -} +import Raytracing; -struct VsOut +cbuffer CB { - float2 texC : TEXCOORD; - float4 posH : SV_POSITION; + uint n; }; -VsOut main(float4 posS : POSITION, float2 texC : TEXCOORD) +StructuredBuffer pos; +StructuredBuffer normal; +RWStructuredBuffer result; + +[numthreads(256, 1, 1)] +void testComputeRayOrigin(uint3 threadId : SV_DispatchThreadID) { - VsOut vOut; - vOut.texC = texC * gScale + gOffset; - vOut.posH = posS; - return vOut; -} \ No newline at end of file + uint i = threadId.x; + if (i >= n) return; + result[i] = computeRayOrigin(pos[i], normal[i]); +} diff --git a/Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cpp new file mode 100644 index 000000000..345475784 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include + +namespace Falcor +{ + namespace + { + using float3 = glm::vec3; + + enum class RayOriginLocation + { + None = 0x0, + Outside = 0x1, + Inside = 0x2 + }; + + float3 getHitPoint(float radius, float3 center) + { + std::mt19937 rng; + auto dist = std::uniform_real_distribution(-1, 1); + auto r = [&]() -> float { return dist(rng); }; + + float x1, x2; + do + { + x1 = r(); + x2 = r(); + } while (x1 * x1 + x2 * x2 >= 1); + + float3 point; + point.x = radius * 2 * x1 * sqrt(1 - x1 * x1 - x2 * x2) + center.x; + point.y = radius * 2 * x2 * sqrt(1 - x1 * x1 - x2 * x2) + center.y; + point.z = radius * (1 - 2 * (x1 * x1 + x2 * x2)) + center.z; + return point; + } + + float3 getRayOrigin(RayOriginLocation loc, float radius, float3 center, float3 hit, bool tangential) + { + std::mt19937 rng; + auto distOut = std::uniform_real_distribution(-20.f, 20.f); + auto rOut = [&]() -> float { return distOut(rng); }; + auto distIn = std::uniform_real_distribution(-radius, radius); + auto rIn = [&]() -> float { return distIn(rng); }; + + float3 normal = center - hit; + float3 shiftedHit = hit - center; + float x, y, z; + + switch (loc) + { + case RayOriginLocation::Outside: + if (tangential) + { + x = rOut(); + y = rOut(); + z = (glm::dot(normal, hit) - normal.x * x - normal.y * y) / normal.z; + return float3(x, y, z); + } + + do + { + x = rOut(); + y = rOut(); + z = rOut(); + } while (x * x + y * y + z * z <= radius * radius || glm::dot(normal, float3(x, y, z) - shiftedHit) >= 0); + break; + case RayOriginLocation::Inside: + do + { + x = rIn(); + y = rIn(); + z = rIn(); + } while (x * x + y * y + z * z >= radius * radius); + break; + default: + should_not_get_here(); + } + + return float3(x, y, z) + center; + } + + float3 getRayDir(bool hasIntersection, float3 origin, float3 hit, bool normalized) + { + if (hit == origin) + { + std::mt19937 rng; + auto dist = std::uniform_real_distribution(-5, 5); + auto r = [&]() -> float { return dist(rng); }; + + auto dir = float3(r(), r(), r()); + return (normalized) ? glm::normalize(dir) : dir; + } + + float3 dir = hit - origin; + if (hasIntersection) + { + return (normalized) ? glm::normalize(dir) : dir; + } + else + { + return (normalized) ? glm::normalize(-dir) : -dir; + } + } + } + + // Just check the first four values. + GPU_TEST(RadicalInverse) + { + ctx.createProgram("Tests/ShadingUtils/ShadingUtilsTests.cs.slang", "testRadicalInverse"); + ctx.allocateStructuredBuffer("result", 4); + ctx["TestCB"]["resultSize"] = 4; + ctx.runProgram(); + + const float *s = ctx.mapBuffer("result"); + EXPECT_EQ(s[0], 0.f); + EXPECT_EQ(s[1], 0.5f); + EXPECT_EQ(s[2], 0.25f); + EXPECT_EQ(s[3], 0.75f); + ctx.unmapBuffer("result"); + } + + GPU_TEST(Random) + { + ctx.createProgram("Tests/ShadingUtils/ShadingUtilsTests.cs.slang", "testRand"); + const int32_t n = 4 * 1024 * 1024; + ctx.allocateStructuredBuffer("result", n); + ctx["TestCB"]["resultSize"] = n; + ctx.runProgram(); + + // A fairly crude test: bucket the range [0,1] into nBuckets buckets + // and make sure that all of them have more or less 1/nBuckets of the + // total values. This doesn't really test the quality of the PRNG very + // well, but will at least detect if it's totally borked. + const float* r = ctx.mapBuffer("result"); + constexpr int32_t nBuckets = 64; + int32_t counts[nBuckets] = { 0 }; + for (int32_t i = 0; i < n; ++i) + { + EXPECT(r[i] >= 0 && r[i] < 1.f) << r[i]; + ++counts[int32_t(r[i] * nBuckets)]; + } + ctx.unmapBuffer("result"); + + for (int32_t i = 0; i < nBuckets; ++i) + { + EXPECT_GT(counts[i], .98 * n / nBuckets); + EXPECT_LT(counts[i], 1.02 * n / nBuckets); + } + } + + GPU_TEST(SphericalCoordinates) + { + ctx.createProgram("Tests/ShadingUtils/ShadingUtilsTests.cs.slang", "testSphericalCoordinates"); + constexpr int32_t n = 1024 * 1024; + ctx.allocateStructuredBuffer("result", n); + ctx["TestCB"]["resultSize"] = n; + // The shader runs threadgroups of 1024 threads. + ctx.runProgram(n); + + // The shader generates a bunch of random vectors, converts them to + // spherical coordinates and back, and computes the dot product with + // the original vector. Here, we'll check that the dot product is + // pretty close to one. + const float* r = ctx.mapBuffer("result"); + for (int32_t i = 0; i < n; ++i) + { + EXPECT_GT(r[i], .999f) << "i = " << i; + EXPECT_LT(r[i], 1.001f) << "i = " << i; + } + ctx.unmapBuffer("result"); + } + + GPU_TEST(RaySphereIntersection) + { + std::mt19937 rng; + auto dist = std::uniform_real_distribution(-10.f, 10.f); + auto r = [&]() -> float { return dist(rng); }; + + std::vector testSphereCenters(12); + std::vector testSphereRadii(12); + std::vector refIsects(12); + std::vector testRayOrigins(12); + std::vector testRayDirs(12); + + for (int32_t i = 0; i < 12; i++) + { + testSphereCenters[i] = glm::vec3(r(), r(), r()); + testSphereRadii[i] = abs(r()); + refIsects[i] = getHitPoint(testSphereRadii[i], testSphereCenters[i]); + switch (i) + { + case 0: + case 1: + testRayOrigins[i] = getRayOrigin(RayOriginLocation::Outside, testSphereRadii[i], testSphereCenters[i], refIsects[i], false); + testRayDirs[i] = getRayDir(true, testRayOrigins[i], refIsects[i], (i % 2 == 0) ? true : false); + break; + case 2: + case 3: + testRayOrigins[i] = getRayOrigin(RayOriginLocation::Outside, testSphereRadii[i], testSphereCenters[i], refIsects[i], true); + testRayDirs[i] = getRayDir(true, testRayOrigins[i], refIsects[i], (i % 2 == 0) ? true : false); + break; + case 4: + case 5: + testRayOrigins[i] = getRayOrigin(RayOriginLocation::Inside, testSphereRadii[i], testSphereCenters[i], refIsects[i], false); + testRayDirs[i] = getRayDir(true, testRayOrigins[i], refIsects[i], (i % 2 == 0) ? true : false); + break; + case 6: + case 7: + testRayOrigins[i] = getRayOrigin(RayOriginLocation::Outside, testSphereRadii[i], testSphereCenters[i], refIsects[i], false); + testRayDirs[i] = getRayDir(false, testRayOrigins[i], refIsects[i], (i % 2 == 0) ? true : false); + break; + case 8: + case 9: + testRayOrigins[i] = getRayOrigin(RayOriginLocation::Outside, testSphereRadii[i], testSphereCenters[i], refIsects[i], false); + testRayDirs[i] = getRayDir(false, testRayOrigins[i], refIsects[i], (i % 2 == 0) ? true : false); + break; + case 10: + case 11: + testRayOrigins[i] = refIsects[i]; + testRayDirs[i] = getRayDir(true, testRayOrigins[i], refIsects[i], (i % 2 == 0) ? true : false); + break; + } + } + + ctx.createProgram("Tests/ShadingUtils/ShadingUtilsTests.cs.slang", "testRaySphereIntersection"); + ctx.allocateStructuredBuffer("sphereCenter", 12, testSphereCenters.data(), testSphereCenters.size() * sizeof(glm::vec3)); + ctx.allocateStructuredBuffer("sphereRadius", 12, testSphereRadii.data()); + ctx.allocateStructuredBuffer("rayOrigin", 12, testRayOrigins.data(), testRayOrigins.size() * sizeof(glm::vec3)); + ctx.allocateStructuredBuffer("rayDir", 12, testRayDirs.data(), testRayDirs.size() * sizeof(glm::vec3)); + ctx.allocateStructuredBuffer("isectResult", 12); + ctx.allocateStructuredBuffer("isectLoc", 12); + ctx["TestCB"]["resultSize"] = 12; + + ctx.runProgram(); + + const uint32_t* result = ctx.mapBuffer("isectResult"); + const glm::vec3* isectLoc = ctx.mapBuffer("isectLoc"); + for (int32_t i = 0; i < 12; i++) + { + switch (i) + { + case 6: + case 7: + case 8: + case 9: + EXPECT_EQ(result[i], 0u); + break; + default: + const float eps = 5e-4f; + EXPECT_EQ(result[i], 1u) << "RaySphereTestCase" << i << ", expected " << 1 << ", got " << result[i]; + EXPECT(abs(isectLoc[i].x - refIsects[i].x) <= eps * (abs(isectLoc[i].x) + abs(refIsects[i].x) + 1.0f)) << "RaySphereTestCase" << i << ", expected " << refIsects[i].x << ", got " << isectLoc[i].x; + EXPECT(abs(isectLoc[i].y - refIsects[i].y) <= eps * (abs(isectLoc[i].y) + abs(refIsects[i].y) + 1.0f)) << "RaySphereTestCase" << i << ", expected " << refIsects[i].y << ", got " << isectLoc[i].y; + EXPECT(abs(isectLoc[i].z - refIsects[i].z) <= eps * (abs(isectLoc[i].z) + abs(refIsects[i].z) + 1.0f)) << "RaySphereTestCase" << i << ", expected " << refIsects[i].z << ", got " << isectLoc[i].z; + } + + // << "RaySphereTestCase" << i << ", expected (" << refIsects[i].x << ", " << refIsects[i].y << ", " << refIsects[i].z << "), got (" << isectLoc[i].x << ", " << isectLoc[i].y << ", " << isectLoc[i].z << ")"; + } + ctx.unmapBuffer("isectResult"); + ctx.unmapBuffer("isectLoc"); + } + +} // namespace Falcor diff --git a/Samples/Utils/FalcorTest/Data/ShadingUtilsTests.cs.slang b/Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cs.slang similarity index 82% rename from Samples/Utils/FalcorTest/Data/ShadingUtilsTests.cs.slang rename to Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cs.slang index 5b3f40f2d..a0b28d394 100644 --- a/Samples/Utils/FalcorTest/Data/ShadingUtilsTests.cs.slang +++ b/Source/Tools/FalcorTest/Tests/ShadingUtils/ShadingUtilsTests.cs.slang @@ -25,21 +25,28 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ - -__import Helpers; +import Helpers; RWStructuredBuffer result; +StructuredBuffer sphereCenter; +StructuredBuffer sphereRadius; +StructuredBuffer rayOrigin; +StructuredBuffer rayDir; +RWStructuredBuffer isectResult; +RWStructuredBuffer isectLoc; + cbuffer TestCB { int resultSize; }; +[numthreads(1, 1, 1)] void testRadicalInverse() { for (int i = 0; i < resultSize; ++i) { - result[i] = radicalInverse(i); + result[i] = radicalInverse(i); } } @@ -71,3 +78,19 @@ void testSphericalCoordinates(uint3 threadId : SV_DispatchThreadID) float2 uv = dirToSphericalCrd(dir); result[threadId.x] = dot(normalize(dir), sphericalCrdToDir(uv)); } + +[numthreads(1, 1, 1)] +void testRaySphereIntersection() +{ + for (int i = 0; i < resultSize; ++i) + { + if (intersectRaySphere(rayOrigin[i], rayDir[i], sphereCenter[i], sphereRadius[i], isectLoc[i])) + { + isectResult[i] = 1; + } + else + { + isectResult[i] = 0; + } + } +} diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangShared.h b/Source/Tools/FalcorTest/Tests/Slang/SlangShared.h new file mode 100644 index 000000000..b4e4455c4 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangShared.h @@ -0,0 +1,62 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#pragma once + +/** Test enum declarations on different forms, shared on CPU/GPU. + + Note that enums in Slang are currently always scoped, using the `.` operator, + `enum Type { A, B };` is therefore the same as `enum class Type { A, B };` + on the GPU side. We should use scoped enums by default as Slang is unlikely + to support unscoped enums anytime soon. + + `enum Type : uint` does currently not work. Add test using it when it does. +*/ + +enum class Type1 +{ + A, + B, + C, + D, +}; + +enum class Type2 +{ + A, + B, + C = 20, + D, +}; + +enum class Type3 +{ + A = 0x01, + B = 0x02, + C = 0x04, + D = 0x08, +}; diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp new file mode 100644 index 000000000..fa23114dd --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp @@ -0,0 +1,182 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ + +/** This file contains tests for Slang language features we depend on. + + The Slang project has its own tests, but it doesn't hurt to test the + common usage inside Falcor to make sure things work for our purposes. +*/ + +#include "Testing/UnitTest.h" +#include "SlangShared.h" +#include + +using half = DirectX::PackedVector::HALF; + +namespace Falcor +{ + namespace + { + void testEnum(GPUUnitTestContext& ctx, const std::string& shaderModel) + { + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testEnum", Program::DefineList(), Shader::CompilerFlags::None, shaderModel); + ctx.allocateStructuredBuffer("result", 12); + ctx.runProgram(1, 1, 1); + + // Verify results. + const uint32_t* result = ctx.mapBuffer("result"); + + EXPECT_EQ(result[0], (uint32_t)Type1::A); + EXPECT_EQ(result[1], (uint32_t)Type1::B); + EXPECT_EQ(result[2], (uint32_t)Type1::C); + EXPECT_EQ(result[3], (uint32_t)Type1::D); + + EXPECT_EQ(result[4], (uint32_t)Type2::A); + EXPECT_EQ(result[5], (uint32_t)Type2::B); + EXPECT_EQ(result[6], (uint32_t)Type2::C); + EXPECT_EQ(result[7], (uint32_t)Type2::D); + + EXPECT_EQ(result[8], (uint32_t)Type3::A); + EXPECT_EQ(result[9], (uint32_t)Type3::B); + EXPECT_EQ(result[10], (uint32_t)Type3::C); + EXPECT_EQ(result[11], (uint32_t)Type3::D); + + ctx.unmapBuffer("result"); + } + + half f32tof16(float fval) { return DirectX::PackedVector::XMConvertFloatToHalf(fval); } + float f16tof32(half hval) { return DirectX::PackedVector::XMConvertHalfToFloat(hval); } + + uint32_t asuint(float32_t a) { return *reinterpret_cast(&a); } + uint64_t asuint64(float64_t a) { return *reinterpret_cast(&a); } + } + + /** Test that compares the enums generated by enum declarations in C++ vs Slang. + + The goal is to verify that the enums can be used interchangeable on the CPU/GPU + without unexpected results. Note in most cases it'd be fine if the enums differ, + but certain uses (flags we OR together etc.) must match. + */ + GPU_TEST(SlangEnum) + { + testEnum(ctx, ""); // Use default shader model for the unit test system + testEnum(ctx, "5_1"); // Test SM 5.1 and higher explicitly + testEnum(ctx, "6_0"); + testEnum(ctx, "6_3"); + } + + /** Test fixed-width scalar type support including 16-bit types (shader model 6.2+). + + This test ensures that Slang supports HLSL 2018 and uses the '-enable-16bit-types' + flag by default in shader model 6.2. + https://github.com/Microsoft/DirectXShaderCompiler/wiki/16-Bit-Scalar-Types + */ + GPU_TEST(SlangScalarTypes) + { + const uint32_t maxTests = 100; + + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testScalarTypes", Program::DefineList(), Shader::CompilerFlags::None, "6_2"); + ctx.allocateStructuredBuffer("result", maxTests); + ctx.runProgram(1, 1, 1); + + // Verify results. + const uint32_t* result = ctx.mapBuffer("result"); + + int i = 0; + + // float16_t + EXPECT_NE(result[i], asuint(1 / 3.f)); + EXPECT_EQ(result[i], asuint(f16tof32(f32tof16(1 / 3.f)))); i++; + + // float32_t + EXPECT_EQ(result[i], asuint(1 / 5.f)); i++; + + // float64_t + EXPECT_EQ(result[i], (uint32_t)asuint64(1 / 7.0)); i++; + EXPECT_EQ(result[i], (uint32_t)(asuint64(1 / 7.0) >> 32)); i++; + + // int16_t + EXPECT_EQ(result[i], (uint32_t)30000); i++; + EXPECT_EQ(result[i], (uint32_t)-3392); i++; + + // int32_t + EXPECT_EQ(result[i], 291123); i++; + EXPECT_EQ(result[i], (uint32_t)-2000000000); i++; + + // int64_t + EXPECT_EQ(result[i], 0xaabbccdd); i++; + EXPECT_EQ(result[i], 0x12345678); i++; + + // uint16_t + EXPECT_EQ(result[i], 59123); i++; + EXPECT_EQ(result[i], 65526); i++; + + // uint32_t + EXPECT_EQ(result[i], 0xfedc1234); i++; + EXPECT_EQ(result[i], (uint32_t)-129); i++; + + // uint64_t + EXPECT_EQ(result[i], 0xaabbccdd); i++; + EXPECT_EQ(result[i], 0x12345678); i++; + + ctx.unmapBuffer("result"); + assert(i < maxTests); + } + + /** Test Slang default initializers for basic types and structs. + */ + GPU_TEST(SlangDefaultInitializers) + { + const uint32_t maxTests = 100, usedTests = 35; + std::vector initData(maxTests, -1); + + auto test = [&](const std::string& shaderModel) + { + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testDefaultInitializers", Program::DefineList(), Shader::CompilerFlags::None, shaderModel); + ctx.allocateStructuredBuffer("result", maxTests, initData.data(), initData.size() * sizeof(initData[0])); + ctx.runProgram(1, 1, 1); + + // Verify results. + const uint32_t* result = ctx.mapBuffer("result"); + for (uint32_t i = 0; i < maxTests; i++) + { + uint32_t expected = i < usedTests ? 0 : -1; + EXPECT_EQ(result[i], expected) << "i = " << i << " (sm" << shaderModel << ")"; + } + ctx.unmapBuffer("result"); + }; + + // Test the default shader model, followed by specific models. + test(""); + test("5_1"); + test("6_0"); + test("6_1"); + test("6_2"); + test("6_3"); + } +} diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cs.slang b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cs.slang new file mode 100644 index 000000000..7ae28e73f --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cs.slang @@ -0,0 +1,228 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "SlangShared.h" + +RWStructuredBuffer result; + +[numthreads(1, 1, 1)] +void testEnum() +{ + // Store the enum values cast to uint. + result[0] = (uint) Type1.A; + result[1] = (uint) Type1.B; + result[2] = (uint) Type1.C; + result[3] = (uint) Type1.D; + + result[4] = (uint) Type2.A; + result[5] = (uint) Type2.B; + result[6] = (uint) Type2.C; + result[7] = (uint) Type2.D; + + result[8] = (uint) Type3.A; + result[9] = (uint) Type3.B; + result[10] = (uint) Type3.C; + result[11] = (uint) Type3.D; +} + +[numthreads(1, 1, 1)] +void testScalarTypes() +{ + int i = 0; + + // 16/32/64-bit floating-point + { + float16_t a = 1 / 3.f; + result[i++] = asuint((float)a); + } + { + float32_t a = 1 / 5.f; + result[i++] = asuint(a); + } + { + // NOTE: Must use 'L' suffix for 64-bit constants, otherwise interpreted as 32-bit. + float64_t a = 1 / 7.0L; + uint lo, hi; + asuint(a, lo, hi); + result[i++] = lo; + result[i++] = hi; + } + + // 16/32/64-bit integer + { + int16_t a = 30000; // 0x00007530 -> 0x7530 (30000) in 16-bit. + int16_t b = -200000; // 0xfffcf2c0 -> Truncated to 0xf2c0 (-3392) in 16-bit. + result[i++] = asuint((int)a); + result[i++] = asuint((int)b); + } + { + int32_t a = 291123; + int32_t b = -2000000000; + result[i++] = asuint(a); + result[i++] = asuint(b); + } + { + int64_t a = 0x12345678aabbccdd; + uint lo = a, hi = a >> 32; + result[i++] = lo; + result[i++] = hi; + } + + // 16/32/64-bit unsigned integer + { + uint16_t a = 59123; // 0x0000e6f3 -> Truncated to 0xe6f3 (59123) + uint16_t b = -10; // 0xfffffff6 -> Truncated to 0xfff6 (65526) + result[i++] = (uint)a; + result[i++] = (uint)b; + } + { + uint32_t a = 0xfedc1234; + uint32_t b = -129; + result[i++] = a; + result[i++] = b; + } + { + uint64_t a = 0x12345678aabbccdd; + uint lo = a, hi = a >> 32; + result[i++] = lo; + result[i++] = hi; + } +} + +struct A +{ + uint a; + float b; +}; + +struct Foo +{ + int a; + uint b; + float c; + bool d; + float2x2 e; + A f; +}; + +[numthreads(1, 1, 1)] +void testDefaultInitializers() +{ + int i = 0; + + // Scalar types (i=0) + { + int a = 1, b = 2, c = {}; + a = {}; + b = c; + result[i++] = a; + result[i++] = b; + result[i++] = c; + } + { + uint a = 1, b = 2, c = {}; + a = {}; + b = c; + result[i++] = a; + result[i++] = b; + result[i++] = c; + } + { + float a = 1, b = 2, c = {}; + a = {}; + b = c; + result[i++] = asuint(a); + result[i++] = asuint(b); + result[i++] = asuint(c); + } +#if 0 + // Fails on SM5.1 probably due to fxc bug with asuint. + { + double a = 1, b = 2, c = {}; + a = {}; + b = c; + asuint(a, result[i], result[i + 1]); i += 2; + asuint(b, result[i], result[i + 1]); i += 2; + asuint(c, result[i], result[i + 1]); i += 2; + } +#endif + + // Vector/matrix types (i=9) + { + float4 a = float4(1, 2, 3, 4), b = {}; + a = {}; + result[i++] = asuint(a.x); + result[i++] = asuint(a.y); + result[i++] = asuint(a.z); + result[i++] = asuint(a.w); + result[i++] = asuint(b.x); + result[i++] = asuint(b.y); + result[i++] = asuint(b.z); + result[i++] = asuint(b.w); + } + { + float2x2 a = float2x2(1, 2, 3, 4), b = {}; + a = {}; + result[i++] = asuint(a[0][0]); + result[i++] = asuint(a[0][1]); + result[i++] = asuint(a[1][0]); + result[i++] = asuint(a[1][1]); + result[i++] = asuint(b[0][0]); + result[i++] = asuint(b[0][1]); + result[i++] = asuint(b[1][0]); + result[i++] = asuint(b[1][1]); + } + + // Struct (i=25) + { + A myA; + myA.a = 1; + myA.b = 2; + Foo foo; + foo.a = 1; + foo.b = 2; + foo.c = 3; + foo.d = true; + foo.e = float2x2(1, 2, 3, 4); + foo.f = myA; + + foo = {}; + + result[i++] = asuint(foo.a); + result[i++] = asuint(foo.b); + result[i++] = asuint(foo.c); + result[i++] = asuint(foo.d); + result[i++] = asuint(foo.e[0][0]); + result[i++] = asuint(foo.e[0][1]); + result[i++] = asuint(foo.e[1][0]); + result[i++] = asuint(foo.e[1][1]); + result[i++] = asuint(foo.f.a); + result[i++] = asuint(foo.f.b); + } + + // All done (i=35) +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp new file mode 100644 index 000000000..c64e2578d --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include + +namespace Falcor +{ + namespace + { + // Some test points. Use exactly representable fp32 values to not worry about numerical issues. + const glm::vec3 kTestData[] = + { + { 1.00f, 2.50f, -0.50f }, + { -3.50f, -0.00f, -1.25f }, + { 4.00f, 2.75f, -2.50f }, + { 0.50f, 1.25f, 4.50f }, + }; + } + + GPU_TEST(AABB) + { + const uint32_t resultSize = 100; + + // Setup and run GPU test. + ctx.createProgram("Tests/Utils/AABBTests.cs.slang", "testAABB"); + ctx.allocateStructuredBuffer("result", resultSize); + ctx.allocateStructuredBuffer("testData", arraysize(kTestData), kTestData, sizeof(kTestData)); + ctx["CB"]["n"] = (uint32_t)arraysize(kTestData); + ctx.runProgram(); + + // Verify results. + const glm::vec3* result = ctx.mapBuffer("result"); + + // TODO: We really wanted to write these tests by incrementing i inside the macro, + // as in `EXPECT_EQ(result[i++], ...)` but that fails as i is incremented multiple times. + size_t i = 0; + + // Test 0 + EXPECT_EQ(result[i], kTestData[0]) << "i = " << i++; + EXPECT_EQ(result[i], kTestData[0]) << "i = " << i++; + EXPECT_EQ(result[i], kTestData[1]) << "i = " << i++; + EXPECT_EQ(result[i], kTestData[2]) << "i = " << i++; + + // Test 1 + EXPECT_EQ(result[i], glm::vec3(1)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(1)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + + EXPECT_EQ(result[i], kTestData[0]) << "i = " << i++; + EXPECT_EQ(result[i], kTestData[0] + glm::vec3(1.f, 1.f, -0.5f)) << "i = " << i++; + + // Test 2 + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(FLT_MAX)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(-FLT_MAX)) << "i = " << i++; + + // Test 3 + EXPECT_EQ(result[i], glm::vec3(-3.50f, 0.00f, -1.25f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3( 1.00f, 2.50f, -0.50f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3( 0.50f, 1.25f, -2.50f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3( 4.00f, 2.75f, 4.50f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(-3.50f, 0.00f, -2.50f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3( 4.00f, 2.75f, 4.50f)) << "i = " << i++; + + // Test 4 + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(1)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(1)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(1)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(1)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + + // Test 5 + EXPECT_EQ(result[i], kTestData[0]) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0)) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.f) << "i = " << i++; + + EXPECT_EQ(result[i], kTestData[0] - glm::vec3(0.f, 0.5f, 0.f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(0.f, 1.f, 0.f)) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.5f) << "i = " << i++; + + EXPECT_EQ(result[i], glm::vec3(0.25f, 1.375f, 1.00f)) << "i = " << i++; + EXPECT_EQ(result[i], glm::vec3(7.50f, 2.75f, 7.00f)) << "i = " << i++; + EXPECT_EQ(result[i].x, 184.75f) << "i = " << i++; + EXPECT_EQ(result[i].x, 144.375f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.5f*std::sqrt(7.50f*7.50f + 2.75f*2.75f + 7.00f*7.00f)) << "i = " << i++; + + // Test 6 + EXPECT_EQ(result[i].x, 1.0f) << "i = " << i; + EXPECT_EQ(result[i].y, 1.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 1.0f) << "i = " << i; + EXPECT_EQ(result[i].y, 1.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 1.0f) << "i = " << i; + EXPECT_EQ(result[i].y, 1.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 1.0f) << "i = " << i; + EXPECT_EQ(result[i].y, 1.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i; + EXPECT_EQ(result[i].y, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i; + EXPECT_EQ(result[i].y, 0.0f) << "i = " << i++; + + // Test 7 + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 2.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 2.5f) << "i = " << i++; + EXPECT_EQ(result[i].x, 5.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 5.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 13.0f) << "i = " << i++; + + // Test 8 + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 0.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 1.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 5.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 5.0f) << "i = " << i++; + EXPECT_EQ(result[i].x, 13.0f) << "i = " << i++; + + assert(i <= resultSize); + ctx.unmapBuffer("result"); + } +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cs.slang b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cs.slang new file mode 100644 index 000000000..25f75e7f5 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cs.slang @@ -0,0 +1,225 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Utils.Math.AABB; + +StructuredBuffer testData; +RWStructuredBuffer result; + +cbuffer CB +{ + uint n; // Number of elements in testData +}; + +[numthreads(1, 1, 1)] +void testAABB(uint3 threadId : SV_DispatchThreadID) +{ + const uint idx = threadId.x; + uint i = 0; + + // Test 0: set() + { + AABB b; + b.set(testData[0]); + result[i++] = b.minPoint; + result[i++] = b.maxPoint; + + b.set(testData[1], testData[2]); + result[i++] = b.minPoint; + result[i++] = b.maxPoint; + } + + // Test 1: valid() + { + AABB b; + b.set(testData[0]); + result[i++] = b.valid() ? 1 : 0; // expect 1 (single point) + b.set(testData[0], testData[0] + 0.5f); + result[i++] = b.valid() ? 1 : 0; // expect 1 (extent is 0.5) + b.set(testData[0], testData[0] + float3(1.f, 1.f, -0.5f)); + result[i++] = b.valid() ? 1 : 0; // expect 0 (z extent is negative) + + result[i++] = b.minPoint; + result[i++] = b.maxPoint; + } + + // Test 2: invalidate() + { + AABB b; + b.set(testData[0], testData[0] + 0.5f); + b.invalidate(); + result[i++] = b.valid() ? 1 : 0; // expect 0 + result[i++] = b.minPoint; + result[i++] = b.maxPoint; + } + + // Test3: include() + { + AABB b0; + b0.invalidate(); + b0.include(testData[0]); + b0.include(testData[1]); + result[i++] = b0.minPoint; + result[i++] = b0.maxPoint; + + AABB b1; + b1.invalidate(); + b1.include(testData[2]); + b1.include(testData[3]); + result[i++] = b1.minPoint; + result[i++] = b1.maxPoint; + + b0.include(b1); + result[i++] = b0.minPoint; + result[i++] = b0.maxPoint; + } + + // Test 4: contains() + { + AABB b; + b.invalidate(); + result[i++] = b.contains(float3(0)) ? 1 : 0; // expect 0 + result[i++] = b.contains(testData[0]) ? 1 : 0; // expect 0 + + b.set(testData[0]); + result[i++] = b.contains(testData[0]) ? 1 : 0; // expect 1 + result[i++] = b.contains(testData[1]) ? 1 : 0; // expect 0 + + b.invalidate(); + for (uint i = 0; i < n; i++) b.include(testData[i]); + result[i++] = b.contains(float3( 1.00f, 1.00f, 1.00f)) ? 1 : 0; // expect 1 + result[i++] = b.contains(float3(-3.50f, 1.00f, 1.00f)) ? 1 : 0; // expect 1 + result[i++] = b.contains(float3(-3.50f, 2.75f, 4.50f)) ? 1 : 0; // expect 1 + result[i++] = b.contains(float3(-3.50f, 2.76f, 4.50f)) ? 1 : 0; // expect 0 + result[i++] = b.contains(float3(-9.50f, 8.00f, 0.00f)) ? 1 : 0; // expect 0 + } + + // Test 5: center(), extent(), area(), volume() + { + AABB b; + b.set(testData[0]); + result[i++] = b.center(); + result[i++] = b.extent(); + result[i++] = b.area(); + result[i++] = b.volume(); + result[i++] = b.radius(); + + b.include(testData[0] - float3(0.f, 1.f, 0.f)); + result[i++] = b.center(); + result[i++] = b.extent(); + result[i++] = b.area(); + result[i++] = b.volume(); + result[i++] = b.radius(); + + b.invalidate(); + for (uint i = 0; i < n; i++) b.include(testData[i]); + result[i++] = b.center(); + result[i++] = b.extent(); + result[i++] = b.area(); + result[i++] = b.volume(); + result[i++] = b.radius(); + } + + // Test 6: intersects() + { + AABB a, b; + a.set(testData[0]); // { 1.00f, 2.50f, -0.50f } + a.include(testData[1]); // { -3.50f, -0.00f, -1.25f } + + b.set(float3(-5.0f, -5.0f, -5.0f), float3(5.0f, 5.0f, 5.0f)); + result[i].x = a.intersects(b); + result[i].y = b.intersects(a); // Expect true (box fully inside larger box) + i++; + + b.set(float3(-4.0f, 2.5f, -0.5f), float3(-3.5f, 3.0f, 0.0f)); + result[i].x = a.intersects(b); + result[i].y = b.intersects(a); // Expect true (box touches corner of other box) + i++; + + b.set(float3(1.0f, 1.0f, -1.0f), float3(2.0f, 1.5f, 0.0f)); + result[i].x = a.intersects(b); + result[i].y = b.intersects(a); // Expect true (box touches side of other box) + i++; + + b.set(float3(1.0f, -5.0f, -5.0f), float3(1.0f, 5.0f, 5.0f)); + result[i].x = a.intersects(b); + result[i].y = b.intersects(a); // Expect true (box touches side of other thin box) + i++; + + b.set(float3(2.0f, -1.0f, -1.0f), float3(100.0f, 100.0f, 100.0f)); + result[i].x = a.intersects(b); + result[i].y = b.intersects(a); // Expect false + i++; + + b.set(float3(0.0f, 1.0f, -0.25f), float3(1.0f, 2.0f, 0.0f)); + result[i].x = a.intersects(b); + result[i].y = b.intersects(a); // Expect false + i++; + } + + // Test 7: minDistance(point) + { + AABB a; + a.set(testData[0]); // { 1.00f, 2.50f, -0.50f } + a.include(testData[1]); // { -3.50f, -0.00f, -1.25f } + + result[i++] = a.minDistance(float3( 0.0f, 1.0f, -1.0f)); // Expect 0.0 (point fully inside) + result[i++] = a.minDistance(float3( 1.0f, 1.0f, -1.0f)); // Expect 0.0 (point on side) + result[i++] = a.minDistance(float3(-3.5f, 0.0f, -0.5f)); // Expect 0.0 (point on corner) + result[i++] = a.minDistance(float3( 3.0f, 0.0f, -0.5f)); // Expect 2.0 (along axis from corner) + result[i++] = a.minDistance(float3( 3.5f, 1.0f, -1.0f)); // Expect 2.5 (along axis from side) + result[i++] = a.minDistance(float3( 5.0f,-3.0f, -0.5f)); // Expect 5.0 (diagonal from corner) + result[i++] = a.minDistance(float3( 5.0f,-3.0f, -1.0f)); // Expect 5.0 (diagonal from side) + result[i++] = a.minDistance(float3( 5.0f,-3.0f, 11.5f)); // Expect 13.0 (diagonal from corner) + } + + // Test 8: minDistance(box) + { + AABB a, b; + a.set(testData[0]); // { 1.00f, 2.50f, -0.50f } + a.include(testData[1]); // { -3.50f, -0.00f, -1.25f } + + // For these expect 0.0 (boxes that touch or intersect). + b.set(float3(-5.0f,-5.0f, -5.0f), float3( 5.0f, 5.0f, 5.0f)); + result[i++] = a.minDistance(b); + b.set(float3(-4.0f, 2.5f, -0.5f), float3(-3.5f, 3.0f, 0.0f)); + result[i++] = a.minDistance(b); + b.set(float3( 1.0f, 1.0f, -1.0f), float3( 2.0f, 1.5f, 0.0f)); + result[i++] = a.minDistance(b); + b.set(float3( 1.0f,-5.0f, -5.0f), float3( 1.0f, 5.0f, 5.0f)); + result[i++] = a.minDistance(b); + + b.set(float3( 2.0f, 0.5f, -1.0f), float3( 2.5f, 1.5f,-0.75f)); + result[i++] = a.minDistance(b); // Expect 1.0 + b.set(float3(-7.5f, 6.5f, -2.0f), float3(-6.5f, 7.5f, 5.0f)); + result[i++] = a.minDistance(b); // Expect 5.0 + b.set(float3(-7.5f, 6.5f, -0.5f), float3(-6.5f, 7.5f, 5.0f)); + result[i++] = a.minDistance(b); // Expect 5.0 + b.set(float3(-7.5f, 6.5f, 11.5f), float3(-6.5f, 7.5f, 12.5f)); + result[i++] = a.minDistance(b); // Expect 13.0 + } +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/AlignedAllocatorTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/AlignedAllocatorTests.cpp new file mode 100644 index 000000000..10b04ae36 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/AlignedAllocatorTests.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Utils/AlignedAllocator.h" + +namespace Falcor +{ + template struct SizedStruct + { + char buf[N]; + }; + + CPU_TEST(AlignedAllocator) + { + AlignedAllocator alloc; + alloc.setMinimumAlignment(16); + alloc.setCacheLineSize(128); + alloc.reserve(1024); + EXPECT_EQ(1024, alloc.getCapacity()); + EXPECT_EQ(0, alloc.getSize()); + + // Do an initial 15 byte allocation. Make sure that everything + // makes sense. + EXPECT_EQ(15, sizeof(SizedStruct<15>)); + void* ptr = alloc.allocate>(); + EXPECT_EQ(15, alloc.getSize()); + EXPECT_EQ(0, alloc.offsetOf(ptr)); + EXPECT_EQ(0, reinterpret_cast(ptr) - reinterpret_cast(alloc.getStartPointer())); + + // Allocate another 8 bytes. Make sure it starts 16-byte aligned. + ptr = alloc.allocate>(); + EXPECT_EQ(24, alloc.getSize()); + EXPECT_EQ(16, alloc.offsetOf(ptr)); + + // Do a one byte allocation and make sure it also starts aligned. + ptr = alloc.allocate(); + EXPECT_EQ(33, alloc.getSize()); + EXPECT_EQ(32, alloc.offsetOf(ptr)); + + // A 100 byte allocation should start at a new cache line now. + ptr = alloc.allocate>(); + EXPECT_EQ(128, alloc.offsetOf(ptr)); + EXPECT_EQ(228, alloc.getSize()); + } + + CPU_TEST(AlignedAllocatorNoCacheLine) + { + AlignedAllocator alloc; + alloc.setMinimumAlignment(16); + alloc.setCacheLineSize(0); // Don't worry about allocations that span cache lines. + alloc.reserve(1024); + EXPECT_EQ(1024, alloc.getCapacity()); + EXPECT_EQ(0, alloc.getSize()); + + void* ptr = alloc.allocate>(); + EXPECT_EQ(64, alloc.getSize()); + EXPECT_EQ(0, alloc.offsetOf(ptr)); + + // Now allocate 72 bytes. It should be immediately after the + // initial allocation since we're already aligned. + ptr = alloc.allocate>(); + EXPECT_EQ(64+72, alloc.getSize()); + EXPECT_EQ(64, alloc.offsetOf(ptr)); + } +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp new file mode 100644 index 000000000..625d86512 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include + +namespace Falcor +{ + namespace + { + // Reference function to interleave m bits from x and y. + // The result is a bit sequence: 0 ... 0 ym xm ... y1 x1 y0 x0. + uint32_t referenceBitInterleave(uint32_t x, uint32_t y, uint32_t m) + { + uint32_t result = 0; + for (uint32_t i = 0; i < m; i++) + { + result |= ((x >> i) & 1) << (2 * i); + result |= ((y >> i) & 1) << (2 * i + 1); + } + return result; + } + } + + GPU_TEST(BitInterleave) + { + const uint32_t tests = 5; + const uint32_t n = 1 << 16; + + // First test the reference function itself against a manually constructed example. + EXPECT_EQ(referenceBitInterleave(0xe38e, 0xbe8b, 16), 0xdeadc0de); + EXPECT_EQ(referenceBitInterleave(0xe38e, 0xbe8b, 12), 0x00adc0de); + + // Create a buffer of random bits to use as test data. + std::vector testData(n); + std::mt19937 r; + for (auto& it : testData) it = r(); + + Buffer::SharedPtr pTestDataBuffer = Buffer::create(n * sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, testData.data()); + if (!pTestDataBuffer) throw ErrorRunningTestException("Failed to allocate buffer"); + + // Setup and run GPU test. + ctx.createProgram("Tests/Utils/BitTricksTests.cs.slang", "testBitInterleave"); + ctx.allocateStructuredBuffer("result", n * tests); + ctx.vars().setRawBuffer("testData", pTestDataBuffer); + ctx.runProgram(n); + + // Verify results. + const uint32_t* result = ctx.mapBuffer("result"); + for (uint32_t i = 0; i < n; i++) + { + const uint32_t bits = testData[i]; + const uint32_t interleavedBits = referenceBitInterleave(bits, bits >> 16, 16); + + // Check result of interleave functions. + EXPECT_EQ(result[tests * i + 0], interleavedBits); + EXPECT_EQ(result[tests * i + 1], (interleavedBits & 0xffff)); + + // Check result of de-interleave functions. + EXPECT_EQ(result[tests * i + 2], (bits & 0x00ff00ff)); + EXPECT_EQ(result[tests * i + 3], (bits & 0x000f000f)); + EXPECT_EQ(result[tests * i + 4], (bits & 0x0f0f0f0f)); + } + ctx.unmapBuffer("result"); + } +} diff --git a/Framework/Source/FalcorExperimental.h b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cs.slang similarity index 54% rename from Framework/Source/FalcorExperimental.h rename to Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cs.slang index 274301643..f9d7b640d 100644 --- a/Framework/Source/FalcorExperimental.h +++ b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cs.slang @@ -25,33 +25,38 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#pragma once +import Utils.Math.BitTricks; -// RenderGraph -#include "Experimental/RenderGraph/RenderGraph.h" -#include "Experimental/RenderGraph/RenderPass.h" -#include "Experimental/RenderGraph/RenderGraphIR.h" -#include "Experimental/RenderGraph/RenderGraphImportExport.h" -#include "Experimental/RenderGraph/RenderGraphUI.h" +ByteAddressBuffer testData; +RWStructuredBuffer result; -// Render Passes -#include "Experimental/RenderPasses/ForwardLightingPass.h" -#include "Experimental/RenderPasses/BlitPass.h" -#include "Experimental/RenderPasses/DepthPass.h" -#include "Experimental/RenderGraph/RenderPassLibrary.h" -#include "Experimental/RenderGraph/RenderGraphImportExport.h" +uint pack(uint2 v) +{ + return (v.y << 16) | v.x; +} -// Raytracing -#ifdef FALCOR_D3D12 -#include "Experimental/Raytracing/RtModel.h" -#include "Experimental/Raytracing/RtScene.h" -#include "Experimental/Raytracing/RtShader.h" -#include "Experimental/Raytracing/RtProgram/RtProgram.h" -#include "Experimental/Raytracing/RtProgram/RtProgramVersion.h" -#include "Experimental/Raytracing/RtProgram/SingleShaderProgram.h" -#include "Experimental/Raytracing/RtProgram/HitProgram.h" -#include "Experimental/Raytracing/RtProgramVars.h" -#include "Experimental/Raytracing/RtState.h" -#include "Experimental/Raytracing/RtStateObject.h" -#include "Experimental/Raytracing/RtSceneRenderer.h" -#endif +[numthreads(256, 1, 1)] +void testBitInterleave(uint3 threadId : SV_DispatchThreadID) +{ + const uint idx = threadId.x; + const uint data = testData.Load(idx * 4); // data = [ y15..y0 x15..x0 ] + + // Use hi/lo words of each dword in the test set as input: + const uint2 bits = uint2(data & 0xffff, data >> 16); // bits.x = [ 0..0 x15..x0 ], bits.y = [ 0..0 y15..y0 ] + const uint interleavedBits = interleave_32bit(bits); // interleavedBits = [y15 x15 .. y0 x0] + + // Test bit interleave. + // Put bogus data in the unused bits to test that it doesn't change the result. + result[5 * idx + 0] = interleave_32bit(bits ^ 0xbeef0000); + result[5 * idx + 1] = interleave_16bit(bits ^ 0xbeefed00); + + // Test bit de-interleave. + // These functions expect zeros for the unused bits. + result[5 * idx + 2] = pack(deinterleave_16bit(interleavedBits & 0x0000ffff)); + result[5 * idx + 3] = pack(deinterleave_8bit(interleavedBits & 0x000000ff)); + + // Test bit de-interleave of two sequences (bits 0-3 and bits 8-11). + // Note: the result is placed in separate 16-bit words, so shift down by 8 bits. + uint2 temp = deinterleave_2x8bit(interleavedBits & 0x00ff00ff); + result[5 * idx + 4] = pack(((temp >> 8) & 0x0f00) | (temp & 0xf)); +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp new file mode 100644 index 000000000..7f27b27ac --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Utils/Algorithm/BitonicSort.h" +#include + +namespace Falcor +{ + namespace + { + // Sort the 'data' array in ascending order within chunks of 'chunkSize' elements. + void sort(std::vector& data, const uint32_t chunkSize) + { + if (chunkSize <= 1) return; + for (size_t first = 0; first < data.size(); first += chunkSize) + { + size_t last = std::min(first + chunkSize, data.size()); + std::sort(data.begin() + first, data.begin() + last); + } + } + + void testGpuSort(GPUUnitTestContext& ctx, BitonicSort* pSort, const uint32_t n, const uint32_t chunkSize) + { + // Create a buffer of random data to use as test data. + std::vector testData(n); + std::mt19937 r; + for (auto& it : testData) it = r(); + + Buffer::SharedPtr pTestDataBuffer = Buffer::create(n * sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, testData.data()); + if (!pTestDataBuffer) throw ErrorRunningTestException("Failed to allocate buffer"); + + // Execute sort on the GPU. + uint32_t groupSize = std::max(chunkSize, 256u); + bool retval = pSort->execute(ctx.getRenderContext(), pTestDataBuffer, n, chunkSize, groupSize); + EXPECT_EQ(retval, true); + + // Sort the test data on the CPU for comparison. + sort(testData, chunkSize); + + // Compare results. + const uint32_t* result = (const uint32_t*)pTestDataBuffer->map(Buffer::MapType::Read); + assert(result); + for (uint32_t i = 0; i < n; i++) + { + EXPECT_EQ(testData[i], result[i]) << "i = " << i; + } + pTestDataBuffer->unmap(); + } + } + +#if _ENABLE_NVAPI + GPU_TEST(BitonicSort) + { + // Create utility class for sorting. + BitonicSort::SharedPtr pSort = BitonicSort::create(); + if (!pSort) throw ErrorRunningTestException("Failed to create BitonicSort object"); + + // Test different parameters. + // The chunk size(last param) must be a pow-of-two <= 1024. + testGpuSort(ctx, pSort.get(), 100, 1); + testGpuSort(ctx, pSort.get(), 19, 2); + testGpuSort(ctx, pSort.get(), 1024, 4); + testGpuSort(ctx, pSort.get(), 11025, 8); + testGpuSort(ctx, pSort.get(), 290, 16); + testGpuSort(ctx, pSort.get(), 1500, 32); + testGpuSort(ctx, pSort.get(), 20000, 64); + testGpuSort(ctx, pSort.get(), 2001, 128); + testGpuSort(ctx, pSort.get(), 16384, 256); + testGpuSort(ctx, pSort.get(), 3103, 1024); + } +#endif +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp new file mode 100644 index 000000000..e4128d8b6 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/ColorUtilsTests.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Utils/Color/ColorUtils.h" +#include + +namespace Falcor +{ + // Some shared test utils. + namespace + { + const float kMaxError = 1e-5f; + + auto maxAbsDiff = [](glm::vec3 a, glm::vec3 b) -> float + { + glm::vec3 d = abs(a - b); + return std::max(std::max(d.x, d.y), d.z); + }; + } + + CPU_TEST(ColorTransforms) + { + const uint32_t n = 10000; + + // Prepare for tests. + std::default_random_engine rng; + auto dist = std::uniform_real_distribution(); + auto u = [&]() -> float { return dist(rng); }; + + const glm::mat3 LMS_CAT02 = kColorTransform_LMStoXYZ_CAT02 * kColorTransform_XYZtoLMS_CAT02; + const glm::mat3 LMS_Bradford = kColorTransform_LMStoXYZ_Bradford * kColorTransform_XYZtoLMS_Bradford; + + // Run test code that transforms random colors between different spaces. + for (uint32_t i = 0; i < n; i++) + { + const glm::vec3 c = { u(), u(), u() }; + + // Test RGB<->XYZ by transforming random colors back and forth. + glm::vec3 res1 = XYZtoRGB_Rec709(RGBtoXYZ_Rec709(c)); + EXPECT_LE(maxAbsDiff(res1, c), kMaxError); + + // Test XYZ<->LMS using the CAT02 transform. + glm::vec3 res2 = LMS_CAT02 * c; + EXPECT_LE(maxAbsDiff(res2, c), kMaxError); + + // Test XYZ<->LMS using the Bradford transform + glm::vec3 res3 = LMS_Bradford * c; + EXPECT_LE(maxAbsDiff(res3, c), kMaxError); + } + } + + CPU_TEST(WhiteBalance) + { + const glm::vec3 white = { 1, 1, 1 }; + + // The white point should be 6500K. Verify that we get pure white back. + glm::vec3 wbWhite = calculateWhiteBalanceTransformRGB_Rec709(6500.f) * white; + EXPECT_LE(maxAbsDiff(wbWhite, white), kMaxError); + + // Test white balance transform at a few different color temperatures. + // This is a very crude test just to see we're not entirely off. + // + // Color correcting white @ 6500K to these targets should yield: + // - Cloudy (7000K) => yellowish tint (r > g > b) + // - Sunny (5500K) => blueish tint (r < g < b) + // - Indoor (3000K) => stronger bluish tint (r < g < b) + glm::vec3 wbCloudy = calculateWhiteBalanceTransformRGB_Rec709(7000.f) * white; + glm::vec3 wbSunny = calculateWhiteBalanceTransformRGB_Rec709(5500.f) * white; + glm::vec3 wbIndoor = calculateWhiteBalanceTransformRGB_Rec709(3000.f) * white; + + EXPECT_GE(wbCloudy.r, wbCloudy.g); + EXPECT_GE(wbCloudy.g, wbCloudy.b); + + EXPECT_LE(wbSunny.r, wbSunny.g); + EXPECT_LE(wbSunny.g, wbSunny.b); + + EXPECT_LE(wbIndoor.r, wbIndoor.g); + EXPECT_LE(wbIndoor.g, wbIndoor.b); + + // Normalize the returned RGB to max 1.0 to be able to compare the scale. + wbSunny /= wbSunny.b; + wbIndoor /= wbIndoor.b; + + EXPECT_LE(wbIndoor.r, wbSunny.r); + EXPECT_LE(wbIndoor.g, wbSunny.g); + } +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp new file mode 100644 index 000000000..893001ca6 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp @@ -0,0 +1,346 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" + +// TODO: Replace DirectXPackedVector.h by a platform-independent alternative that +// allows configuring the rounding modes to match what we detect on the GPU. +#include +using half = DirectX::PackedVector::HALF; + +/** Notes on IEEE 754 fp16 floating-point representation: + + 1 sign bit, 5 exponent bits, 10 mantissa bits. + + If exponent is 11111: mantissa==0 is +-inf, mantissa!=0 it's NaN. + If exponent is 00000: mantissa==0 it's +-0, mantissa!=0 it's a denorm with exponent 2^-14. + Else normalized numbers with exponent offset by 15: value = sign x 2^(exp-15) x 1.mantissa. + + Finite positive fp16 numbers are encoded in [0x0000, 0x7c00). In signed decimal [0,31744). + The fp16 values are in strictly increasing order from 0.0 to 65504.0. + + Finite negative fp16 numbers are encoded in [0x8000, 0xfc00). In signed decimal [-32768, -1024). + The fp16 values are in strictly decreasing order from -0.0 to -65504.0. + + See also https://en.wikipedia.org/wiki/Half-precision_floating-point_format +*/ + +namespace Falcor +{ + namespace + { + /** Converts a fp32 number to f16 using the default rounding mode. + TODO: Analyze what the default is on the GPU, make sure the CPU lib matches. + */ + half f32tof16(float fval) + { + return DirectX::PackedVector::XMConvertFloatToHalf(fval); + } + + /** Converts a fp16 number to f32. + */ + float f16tof32(half hval) + { + return DirectX::PackedVector::XMConvertHalfToFloat(hval); + } + + /** Converts a finite fp32 number to fp16, rounding down to the nearest representable number. + */ + half f32tof16_roundDown(float value) + { + half h = f32tof16(value); + float res = f16tof32(h); + if (res > value) + { + // Result was rounded up. + // Depending on the value, the next smaller fp16 number is given by: + // res < 0: +1 gives the next smaller + // res > 0: -1 gives the next smaller + // res == +-0: next smaller is 0x8001 + if (res < 0.f) h++; + else if (res > 0.f) h--; + else h = 0x8001; + } + return h; + } + + /** Converts a finite fp32 number to fp16, rounding up to the nearest representable number. + */ + half f32tof16_roundUp(float value) + { + half h = f32tof16(value); + float res = f16tof32(h); + if (res < value) + { + // Result was rounded down. + // Depending on the value, the next larger fp16 number is given by: + // res < 0: -1 gives the next larger + // res > 0: +1 gives the next larger + // res == +-0: next larger is 0x0001 + if (res < 0.f) h--; + else if (res > 0.f) h++; + else h = 0x0001; + } + return h; + } + + /** Returns true if fp32 number can be exactly represented in fp16. + */ + bool isExactFP16(float v) { return f16tof32(f32tof16(v)) == v; } + +#if 0 + // TODO: Currently disabled until we figure out the rounding modes and have a matching CPU library. + /** Generates test data to verify round-to-nearest-even in fp32->fp16 conversion. + */ + void generateFP16RNETestData(std::vector& input, std::vector& expected) + { + { + float a = 0x1.0040p0f; // 1.0000 0000 01 00 0000 = 1 + 64/65536 (exactly representable) + float b = 0x1.005fp0f; // 1.0000 0000 01 01 1111 = 1 + 95/65536 (slightly under) + float c = 0x1.0060p0f; // 1.0000 0000 01 10 0000 = 1 + 96/65536 (half-way) + float d = 0x1.0061p0f; // 1.0000 0000 01 10 0001 = 1 + 97/65536 (slightly over) + float e = 0x1.0080p0f; // 1.0000 0000 10 00 0000 = 1 + 128/65536 (exactly representable) + + input.push_back(a); + input.push_back(b); + input.push_back(c); + input.push_back(d); + input.push_back(e); + + // Expected result using round-to-nearest even. + expected.push_back(a); + expected.push_back(a); + expected.push_back(e); // Rounded up to nearest even mantissa + expected.push_back(e); + expected.push_back(e); + } + { + float a = 0x1.0080p0f; // 1.0000 0000 1000 0000 = 1 + 128/65536 (exactly representable) + float b = 0x1.00dfp0f; // 1.0000 0000 1001 1111 = 1 + 223/65536 (slightly under) + float c = 0x1.00e0p0f; // 1.0000 0000 1110 0000 = 1 + 224/65536 (half-way) + float d = 0x1.00e1p0f; // 1.0000 0000 1110 0001 = 1 + 225/65536 (slightly over) + float e = 0x1.0100p0f; // 1.0000 0001 0000 0000 = 1 + 256/65536 (exactly representable) + + input.push_back(a); + input.push_back(b); + input.push_back(c); + input.push_back(d); + input.push_back(e); + + // Expected result using round-to-nearest even. + expected.push_back(a); + expected.push_back(a); + expected.push_back(a); // Rounded down to nearest even mantissa + expected.push_back(e); + expected.push_back(e); + } + } +#endif + + std::vector generateAllFiniteFP16() + { + std::vector data; + + // Loop over all finite numbers in fp16. + for (uint32_t i = 0; i < 0xfc00; i++) + { + if (i >= 0x7c00 && i < 0x8000) continue; // Skip special values (inf, nan). + assert(i <= 0xffff); + data.push_back(i); + } + return data; + } + + std::vector generateFP16TestData(UnitTestContext& ctx) + { + std::vector data; + + // Loop over all finite numbers in fp16. + // Test that the exact number is unmodified by fp16 rounding. + // Test that the number +- epsilon rounds correctly. + for (uint16_t i = 0; i < 0xfc00; i++) + { + if (i >= 0x7c00 && i < 0x8000) continue; // Skip special values (inf, nan). + const float exact = f16tof32(i); + + // Compute numbers that are lighly smaller/larger than the exact fp32 value. + float x = exact * (1.f + FLT_EPSILON); + if (x != 0.f) EXPECT_NE(exact, x); + + float y = exact * (1.f - FLT_EPSILON); + if (x != 0.f) EXPECT_NE(exact, y); + + // Store test values. + data.push_back(exact); + data.push_back(x); + data.push_back(y); + } + + return data; + } + } + +#if 0 + // This test currently fails due to difference in rounding modes for f32tof16() between CPU and GPU. + // TODO: Currently disabled until we figure out the rounding modes and have a matching CPU library. + GPU_TEST(FP32ToFP16Conversion) + { + std::vector testData = generateFP16TestData(ctx); + + ctx.createProgram("Tests/Utils/HalfUtilsTests.cs.slang", "testFP32ToFP16"); + ctx.allocateStructuredBuffer("inputFloat", testData.size(), testData.data(), testData.size() * sizeof(decltype(testData)::value_type)); + ctx.allocateStructuredBuffer("resultUint", testData.size()); + ctx["CB"]["testSize"] = (uint32_t)testData.size(); + ctx.runProgram((uint32_t)testData.size(), 1, 1); + + // Verify results. + const uint32_t* result = ctx.mapBuffer("resultUint"); + for (size_t i = 0; i < testData.size(); i++) + { + const float v = testData[i]; + EXPECT_EQ(result[i], (uint32_t)f32tof16(v)) << "v = " << v << " (i = " << i << ")"; + } + ctx.unmapBuffer("resultUint"); + } +#endif + + GPU_TEST(FP16ToFP32Conversion) + { + std::vector testData = generateAllFiniteFP16(); + + ctx.createProgram("Tests/Utils/HalfUtilsTests.cs.slang", "testFP16ToFP32"); + ctx.allocateStructuredBuffer("inputUint", testData.size(), testData.data(), testData.size() * sizeof(decltype(testData)::value_type)); + ctx.allocateStructuredBuffer("resultFloat", testData.size()); + ctx["CB"]["testSize"] = (uint32_t)testData.size(); + ctx.runProgram((uint32_t)testData.size(), 1, 1); + + // Verify results. + const float* result = ctx.mapBuffer("resultFloat"); + for (size_t i = 1000; i < testData.size(); i++) + { + const uint32_t v = testData[i]; + EXPECT_EQ(result[i], f16tof32(v)) << "v = " << v << " (i = " << i << ")"; + } + ctx.unmapBuffer("resultFloat"); + } + + /** Test our CPU-side functions for f32tof16 conversion with conservative rounding. + */ + CPU_TEST(FP32ToFP16ConservativeRoundingCPU) + { + // Test assumptions on fp16 encoding. + EXPECT_EQ(f16tof32(0x0000), 0.f); + EXPECT_EQ(f16tof32(0x8000), -0.f); + EXPECT_EQ(f16tof32(0x7c00), std::numeric_limits::infinity()); + EXPECT_EQ(f16tof32(0xfc00), -std::numeric_limits::infinity()); + + // Test f32->f16 rounding functions on the CPU. + std::vector testData = generateFP16TestData(ctx); + for (size_t i = 0; i < testData.size(); i++) + { + const float v = testData[i]; + if (isExactFP16(v)) + { + // Make sure fp32 numbers exactly representable in fp16 are unmodified by rounding. + EXPECT_EQ(f16tof32(f32tof16_roundUp(v)), v) << "i = " << i; + EXPECT_EQ(f16tof32(f32tof16_roundDown(v)), v) << "i = " << i; + } + else + { + // Make sure fp32 numbers not-exactly representably in fp16 are conservatively rounded. + EXPECT_GE(f16tof32(f32tof16_roundUp(v)), v) << "i = " << i; + EXPECT_LE(f16tof32(f32tof16_roundDown(v)), v) << "i = " << i; + } + } + } + + /** Test our GPU-side utils for f32tof16 conversion with conservative rounding. + The test is written so that the conversion to fp16 is done on the GPU and the conversion + back to fp32 on the CPU, to avoid shader compiler optimizations for interfering with the results. + */ + GPU_TEST(FP32ToFP16ConservativeRoundingGPU) + { + std::vector testData = generateFP16TestData(ctx); + + ctx.createProgram("Tests/Utils/HalfUtilsTests.cs.slang", "testFP32ToFP16ConservativeRounding"); + ctx.allocateStructuredBuffer("inputFloat", testData.size(), testData.data(), testData.size() * sizeof(decltype(testData)::value_type)); + ctx.allocateStructuredBuffer("resultUint", testData.size() * 2); + ctx["CB"]["testSize"] = (uint32_t)testData.size(); + ctx.runProgram((uint32_t)testData.size(), 1, 1); + + // Verify results. + const uint32_t* result = ctx.mapBuffer("resultUint"); + + for (size_t i = 0; i < testData.size(); i++) + { + const float v = testData[i]; + if (isExactFP16(v)) + { + // Make sure fp32 numbers exactly representable in fp16 are unmodified by rounding. + EXPECT_EQ(f16tof32(result[2 * i + 0]), v) << "i = " << i; + EXPECT_EQ(f16tof32(result[2 * i + 1]), v) << "i = " << i; + } + else + { + // Make sure fp32 numbers not-exactly representably in fp16 are conservatively rounded. + EXPECT_GE(f16tof32(result[2 * i + 0]), v) << "i = " << i; + EXPECT_LE(f16tof32(result[2 * i + 1]), v) << "i = " << i; + } + } + + ctx.unmapBuffer("resultUint"); + } + +#if 0 + // TODO: Currently disabled until we figure out the rounding modes and have a matching CPU library. + // TODO: Look into the spec (is it even strictly spec'ed in HLSL?) and add utility function to detect the mode used. + GPU_TEST(FP16RoundingModeGPU) + { + std::vector input, expected; + generateFP16RNETestData(input, expected); + + // TODO: The precise flag does not seem to be respected on pre-SM6.2 for this shader + // The computation of the quantized value using 'y = f16tof32(f32tof16(x))' gets optimized to 'y = x' in the shader, despite the global precise flag. + ctx.createProgram("Tests/Utils/HalfUtilsTests.cs.slang", "testFP16RoundingMode", Program::DefineList(), Shader::CompilerFlags::FloatingPointModePrecise, "6_2"); + ctx.allocateStructuredBuffer("inputFloat", input.size(), input.data(), input.size() * sizeof(decltype(input)::value_type)); + ctx.allocateStructuredBuffer("resultFloat", expected.size()); + ctx["CB"]["testSize"] = (uint32_t)input.size(); + ctx.runProgram((uint32_t)input.size(), 1, 1); + + // Verify results. + const float* result = ctx.mapBuffer("resultFloat"); + + for (size_t i = 0; i < expected.size(); i++) + { + float v = result[i]; + EXPECT_EQ(result[i], expected[i]) << "i = " << i; + } + + ctx.unmapBuffer("resultFloat"); + } +#endif +} diff --git a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.h b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cs.slang similarity index 52% rename from Samples/Core/MultiPassPostProcess/MultiPassPostProcess.h rename to Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cs.slang index 9ba348d98..e0db3a166 100644 --- a/Samples/Core/MultiPassPostProcess/MultiPassPostProcess.h +++ b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,30 +25,59 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -#pragma once -#include "Falcor.h" +import Utils.Math.HalfUtils; -using namespace Falcor; +cbuffer CB +{ + uint testSize; +} + +StructuredBuffer inputFloat; +StructuredBuffer inputUint; + +RWStructuredBuffer resultUint; +RWStructuredBuffer resultFloat; + +/** Converts a sequence of fp32 to fp16 using default rounding. +*/ +[numthreads(256, 1, 1)] +void testFP32ToFP16(uint3 threadId : SV_DispatchThreadID) +{ + uint i = threadId.x; + if (i >= testSize) return; + resultUint[i] = f32tof16(inputFloat[i]); +} + +/** Converts a sequence of fp16 numbers to fp32. +*/ +[numthreads(256, 1, 1)] +void testFP16ToFP32(uint3 threadId : SV_DispatchThreadID) +{ + uint i = threadId.x; + if (i >= testSize) return; + resultFloat[i] = f16tof32(inputUint[i]); +} + +/** Converts a sequence of fp32 numbers to fp16 using conservative rounding up/down. +*/ +[numthreads(256, 1, 1)] +void testFP32ToFP16ConservativeRounding(uint3 threadId : SV_DispatchThreadID) +{ + uint i = threadId.x; + if (i >= testSize) return; + + float v = inputFloat[i]; + resultUint[2 * i + 0] = f32tof16_roundUp(v); + resultUint[2 * i + 1] = f32tof16_roundDown(v); +} -class MultiPassPostProcess : public Renderer +#if 0 +// TODO: Currently disabled until we figure out the rounding modes and have a matching CPU library. +[numthreads(256, 1, 1)] +void testFP16RoundingMode(uint3 threadId : SV_DispatchThreadID) { -public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onShutdown(SampleCallbacks* pSample) override; - bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; -private: - Texture::SharedPtr mpImage; - Fbo::SharedPtr mpTempFB; - - FullScreenPass::UniquePtr mpLuminance; - GaussianBlur::UniquePtr mpGaussianBlur; - FullScreenPass::UniquePtr mpBlit; - GraphicsVars::SharedPtr mpProgVars; - - bool mEnableGaussianBlur = false; - bool mEnableGrayscale = false; - void loadImage(SampleCallbacks* pSample); - void loadImageFromFile(SampleCallbacks* pSample, std::string filename); -}; + uint i = threadId.x; + if (i >= testSize) return; + resultFloat[i] = f16tof32(f32tof16(inputFloat[i])); // TODO: Use precise keyword when available. +} +#endif diff --git a/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp new file mode 100644 index 000000000..17019f70a --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include + +// The perfect hash tests are disabled by default as they take a really long time to run. +// We test a subset of the space instead. The full tests are useful to re-run if the hash is modified. +//#define RUN_PERFECT_HASH_TESTS + +namespace Falcor +{ + namespace + { + /** Jenkins hash. This should match HashUtils.slang. + */ + uint32_t jenkinsHash(uint32_t a) + { + a = (a + 0x7ed55d16) + (a << 12); + a = (a ^ 0xc761c23c) ^ (a >> 19); + a = (a + 0x165667b1) + (a << 5); + a = (a + 0xd3a2646c) ^ (a << 9); + a = (a + 0xfd7046c5) + (a << 3); + a = (a ^ 0xb55a4f09) ^ (a >> 16); + return a; + } + } + + GPU_TEST(JenkinsHash_CompareToCPU) + { + // Allocate results buffer (64k dwords). + TypedBuffer::SharedPtr pResultBuffer = TypedBuffer::create(1 << 16, ResourceBindFlags::UnorderedAccess); + ctx.getRenderContext()->clearUAV(pResultBuffer->getUAV().get(), glm::uvec4(0)); + + // Setup and run GPU test. + ctx.createProgram("Tests/Utils/HashUtilsTests.cs.slang", "testJenkinsHash"); + ctx.vars().setTypedBuffer("result", pResultBuffer); + ctx.runProgram(1 << 16, 1, 1); + + // Verify that the generated hashes match the CPU version. + const uint32_t* result = (const uint32_t*)pResultBuffer->map(Buffer::MapType::Read); + assert(result); + for (uint32_t i = 0; i < pResultBuffer->getElementCount(); i++) + { + EXPECT_EQ(result[i], jenkinsHash(i)) << "i = " << i; + } + pResultBuffer->unmap(); + } + +#ifdef RUN_PERFECT_HASH_TESTS + CPU_TEST(JenkinsHash_PerfectHashCPU) + { + std::vector result(1 << 27, 0); + for (uint64_t i = 0; i < (1ull << 32); i++) + { + uint32_t h = jenkinsHash((uint32_t)i); + result[h >> 5] |= 1u << (h & 0x1f); + } + for (size_t i = 0; i < result.size(); i++) + { + EXPECT_EQ(result[i], 0xffffffff) << "i = " << i; + } + } +#endif + +#ifdef RUN_PERFECT_HASH_TESTS + GPU_TEST(JenkinsHash_PerfectHashGPU) + { + // Allocate results buffer (2^27 dwords). + TypedBuffer::SharedPtr pResultBuffer = TypedBuffer::create(1 << 27, ResourceBindFlags::UnorderedAccess); + ctx.getRenderContext()->clearUAV(pResultBuffer->getUAV().get(), glm::uvec4(0)); + + // Setup and run GPU test. + ctx.createProgram("Tests/Utils/HashUtilsTests.cs.slang", "testJenkinsHash_PerfectHash"); + ctx.vars().setTypedBuffer("result", pResultBuffer); + ctx.runProgram(1 << 16, 1 << 16, 1); + + // Verify that all possible 32-bit hashes has occured (all bits set). + const uint32_t* result = (const uint32_t*)pResultBuffer->map(Buffer::MapType::Read); + assert(result); + for (uint32_t i = 0; i < pResultBuffer->getElementCount(); i++) + { + EXPECT_EQ(result[i], 0xffffffff) << "i = " << i; + } + pResultBuffer->unmap(); + } +#endif +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cs.slang b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cs.slang new file mode 100644 index 000000000..f9d03c486 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cs.slang @@ -0,0 +1,50 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +import Utils.Math.HashUtils; + +RWBuffer result; + +[numthreads(16, 16, 1)] +void testJenkinsHash_PerfectHash(uint3 threadId : SV_DispatchThreadID) +{ + // Test that hash function is a perfect hash (no collisions). + // We run 2^32 threads, and record the output as 1 bit per possible value (2^27 dwords). + uint i = (threadId.y << 16) | threadId.x; // i in [0, 2^32) + uint h = jenkinsHash(i); + + uint k = h >> 5; + uint b = h & 0x1f; + InterlockedOr(result[k], 1u << b); +} + +[numthreads(16, 16, 1)] +void testJenkinsHash(uint3 threadId : SV_DispatchThreadID) +{ + uint i = (threadId.y << 16) | threadId.x; + result[i] = jenkinsHash(i); +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp new file mode 100644 index 000000000..353d102c8 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cpp @@ -0,0 +1,268 @@ +/*************************************************************************** +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include +#include +#include +#include +#include + +namespace Falcor +{ + namespace + { + struct BBoxTestCase + { + float3 origin, aabbMin, aabbMax; + float angle; + }; + + void runBBoxTestComputeShader(GPUUnitTestContext& ctx, const BBoxTestCase* testCases, int nTests, const char* entrypoint) + { + TypedBuffer::SharedPtr pOriginBuffer = TypedBuffer::create(nTests); + TypedBuffer::SharedPtr pAABBMinBuffer = TypedBuffer::create(nTests); + TypedBuffer::SharedPtr pAABBMaxBuffer = TypedBuffer::create(nTests); + if (!pOriginBuffer ||!pAABBMinBuffer || !pAABBMaxBuffer) throw ErrorRunningTestException("Failed to allocate buffer"); + for (int i = 0; i < nTests; ++i) + { + pOriginBuffer->setElement(i, testCases[i].origin); + pAABBMinBuffer->setElement(i, testCases[i].aabbMin); + pAABBMaxBuffer->setElement(i, testCases[i].aabbMax); + } + + // Setup and run GPU test. + ctx.createProgram("Tests/Utils/MathHelpersTests.cs.slang", entrypoint); + ctx.vars().setTypedBuffer("origin", pOriginBuffer); + ctx.vars().setTypedBuffer("aabbMin", pAABBMinBuffer); + ctx.vars().setTypedBuffer("aabbMax", pAABBMaxBuffer); + ctx.allocateStructuredBuffer("sinTheta", nTests); + ctx.allocateStructuredBuffer("cosTheta", nTests); + ctx.allocateStructuredBuffer("coneDir", nTests); + ctx.runProgram(nTests); + } + + void testKnownBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) + { + // Generate test data... + BBoxTestCase testCases[] = + { + // Unit box centered at z=2 -> then tan(theta) is the ratio of + // half of the length of the diagonal of a box face (aka + // sqrt(2) / 2), divided by the distance from |origin| to the + // box (aka 2). + { float3(0., 0., 0.), float3(-0.5f, -0.5f, 2.f), float3(0.5f, 0.5f, 3.f), std::atan2(std::sqrt(2.f) / 2.f, 2.f) }, + // Point is inside the box + { float3(0.5f, 10.f, -20.f), float3(-.25f, 5.f, -22.f), float3(3.f, 17.f, 29.f), static_cast(M_PI) } + }; + int nTests = sizeof(testCases) / sizeof(testCases[0]); + + runBBoxTestComputeShader(ctx, testCases, nTests, entrypoint); + + const float* sinTheta = ctx.mapBuffer("sinTheta"); + const float* cosTheta = ctx.mapBuffer("cosTheta"); + + for (int i = 0; i < nTests; ++i) + { + const BBoxTestCase& tc = testCases[i]; + if (tc.angle == static_cast(M_PI)) + { + // Expect to get it exact for points inside the box. + EXPECT_EQ(sinTheta[i], 0.f); + EXPECT_EQ(cosTheta[i], -1.f); + } + else + { + const float eps = 1e-4f; + EXPECT(std::sin(testCases[i].angle) > (1.f - eps) * sinTheta[i] && + std::sin(testCases[i].angle) < (1.f + eps) * sinTheta[i]) << + "BBoxTestCase " << i << ", expected sin(theta) = " << std::sin(testCases[i].angle) << ", got " << sinTheta[i]; + EXPECT(std::cos(testCases[i].angle) > (1.f - eps) * cosTheta[i] && + std::cos(testCases[i].angle) < (1.f + eps) * cosTheta[i]) << + "BBoxTestCase " << i << ", expected cos(theta) = " << std::cos(testCases[i].angle) << ", got " << cosTheta[i]; + } + } + + ctx.unmapBuffer("cosTheta"); + ctx.unmapBuffer("sinTheta"); + } + + void testRandomBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) + { + // Generate test data. + std::vector testCases; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> posAndNegDist(-100.f, 100.f); + std::uniform_real_distribution<> posDist(1e-4f, 100.f); + for (int i = 0; i < 256; ++i) + { + // Random origins and bounding boxes. + float3 origin(posAndNegDist(gen), posAndNegDist(gen), posAndNegDist(gen)); + float3 aabbMin(posAndNegDist(gen), posAndNegDist(gen), posAndNegDist(gen)); + float3 aabbMax = aabbMin + float3(posDist(gen), posDist(gen), posDist(gen)); + + testCases.push_back({origin, aabbMin, aabbMax, 0.f}); // angle is unused in this test. + } + + runBBoxTestComputeShader(ctx, testCases.data(), static_cast(testCases.size()), entrypoint); + + const float* sinTheta = ctx.mapBuffer("sinTheta"); + const float* cosTheta = ctx.mapBuffer("cosTheta"); + const float3* coneDir = ctx.mapBuffer("coneDir"); + + for (size_t i = 0; i < testCases.size(); ++i) + { + const BBoxTestCase &b = testCases[i]; + bool inside = (b.origin.x >= b.aabbMin.x && b.origin.x <= b.aabbMax.x && + b.origin.y >= b.aabbMin.y && b.origin.y <= b.aabbMax.y && + b.origin.z >= b.aabbMin.z && b.origin.z <= b.aabbMax.z); + if (inside) + { + // Expect to get it exact for points inside the box. + EXPECT_EQ(sinTheta[i], 0.f); + EXPECT_EQ(cosTheta[i], -1.f); + } + else + { + float minCosTheta = 1.f; + for (int j = 0; j < 8; ++j) + { + // Make sure that the vector to AABB corner is inside the cone. + float3 corner = float3((j & 1) ? b.aabbMin.x : b.aabbMax.x, + (j & 2) ? b.aabbMin.y : b.aabbMax.y, + (j & 4) ? b.aabbMin.z : b.aabbMax.z); + float3 v = normalize(corner - b.origin); + float ct = dot(v, normalize(coneDir[i])); + EXPECT_GT(ct, (cosTheta[i] > 0 ? (0.99f * cosTheta[i]) : (1.01f * cosTheta[i]))); + minCosTheta = std::min(minCosTheta, ct); + } + // Make sure that the maximum angle between a vector to an AABB corner and the + // cone axis isn't much bigger than the reported cone axis. + EXPECT_LT(minCosTheta, (cosTheta[i] > 0 ? (1.01f * cosTheta[i]) : (0.99f * cosTheta[i]))); + } + } + + ctx.unmapBuffer("cosTheta"); + ctx.unmapBuffer("sinTheta"); + } + } + + GPU_TEST(BoxSubtendedConeAngleCenter) + { + testKnownBBoxes(ctx, "testBoundingConeAngleCenter"); + } + + GPU_TEST(BoxSubtendedConeAngleAverage) + { + testKnownBBoxes(ctx, "testBoundingConeAngleAverage"); + } + +#if 0 + // Disable this test for now: it turns out that this bounding method returns + // cos(theta) = -1 for points that are close enough to the bounding box that + // their cos(theta) value is < 0. + GPU_TEST(BoxSubtendedConeAngleCenterRandoms) + { + testRandomBBoxes(ctx, "testBoundingConeAngleCenter"); + } +#endif + + GPU_TEST(BoxSubtendedConeAngleAverageRandoms) + { + testRandomBBoxes(ctx, "testBoundingConeAngleAverage"); + } + + GPU_TEST(SphereSubtendedAngle) + { + // Generate test data... + struct TestCase + { + float3 origin; + float radius; + float angle; + }; + TestCase testCases[] = + { + // sin(theta) between a vector to the center of the sphere and a + // vector that is tangent to the sphere is the sphere radius + // divided by the distance from the starting point to the center + // of the sphere. + { float3(0.f, 0.f, 2.f), 1.f, std::asin(1.f / 2.f) }, + { float3(10.f, -5.f, 2.f), 0.1f, std::asin(0.1f / std::sqrt(10.f*10.f + 5.f*5.f + 2.f*2.f)) }, + // Point inside the sphere. + { float3(0.5f, 0.f, 0.f), 0.51f, static_cast(M_PI) } + }; + int nTests = sizeof(testCases) / sizeof(testCases[0]); + + TypedBuffer::SharedPtr pTestCaseBuffer = TypedBuffer::create(nTests); + if (!pTestCaseBuffer) throw ErrorRunningTestException("Failed to allocate buffer"); + for (int i = 0; i < nTests; ++i) + { + // Pack sphere origins and radii into float4s. + pTestCaseBuffer->setElement(i, float4(testCases[i].origin, testCases[i].radius)); + } + + // Setup and run GPU test. + ctx.createProgram("Tests/Utils/MathHelpersTests.cs.slang", "testBoundSphereAngle"); + ctx.vars().setTypedBuffer("spheres", pTestCaseBuffer); + ctx.allocateStructuredBuffer("sinTheta", nTests); + ctx.allocateStructuredBuffer("cosTheta", nTests); + ctx.runProgram(nTests); + + const float* sinTheta = ctx.mapBuffer("sinTheta"); + const float* cosTheta = ctx.mapBuffer("cosTheta"); + + for (int i = 0; i < nTests; ++i) + { + const TestCase& tc = testCases[i]; + if (tc.angle == static_cast(M_PI)) + { + // Expect to get it exact for points inside the sphere. + EXPECT_EQ(sinTheta[i], 0.f); + EXPECT_EQ(cosTheta[i], -1.f); + } + else + { + const float eps = 1e-4f; + EXPECT(std::sin(testCases[i].angle) > (1.f - eps) * sinTheta[i] && + std::sin(testCases[i].angle) < (1.1 + eps) * sinTheta[i]) << + "Expected sin(theta) = " << std::sin(testCases[i].angle) << ", got " << sinTheta[i] << + ", for sphere at (" << tc.origin.x << ", " << tc.origin.y << ", " << tc.origin.z << + "), radius " << tc.radius; + EXPECT(std::cos(testCases[i].angle) > (1.f - eps) * cosTheta[i] && + std::cos(testCases[i].angle) < (1.f + eps) * cosTheta[i]) << + "Expected cos(theta) = " << std::cos(testCases[i].angle) << ", got " << cosTheta[i] << + ", for sphere at (" << tc.origin.x << ", " << tc.origin.y << ", " << tc.origin.z << + "), radius " << tc.radius; + } + } + + ctx.unmapBuffer("cosTheta"); + ctx.unmapBuffer("sinTheta"); + } +} diff --git a/Samples/Raytracing/PathTracer/Data/GGXGIShadowRay.slang b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cs.slang similarity index 59% rename from Samples/Raytracing/PathTracer/Data/GGXGIShadowRay.slang rename to Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cs.slang index 2abd509a4..440a45902 100644 --- a/Samples/Raytracing/PathTracer/Data/GGXGIShadowRay.slang +++ b/Source/Tools/FalcorTest/Tests/Utils/MathHelpersTests.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,28 +25,34 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -import ShaderCommon; -import Raytracing; -import GGXGICommon; +import Utils.Math.MathHelpers; -// What code is executed when our ray misses all geometry? -[shader("miss")] -void ShadowMiss(inout ShadowRayPayload rayData) +Buffer spheres; // xyz: center, w: radius +RWStructuredBuffer sinTheta, cosTheta; + +[numthreads(1, 1, 1)] +void testBoundSphereAngle(uint3 threadId : SV_DispatchThreadID) { - // If we miss all geometry, then the light is visibile - rayData.visFactor = 1.0f; + const uint index = threadId.x; + boundSphereSubtendedConeAngle(spheres[index].xyz, spheres[index].w, + sinTheta[index], cosTheta[index]); } -// What code is executed when our ray hits a potentially transparent surface? -[shader("anyhit")] -void ShadowAnyHit(inout ShadowRayPayload rayData, BuiltInTriangleIntersectionAttributes attribs) +Buffer origin, aabbMin, aabbMax; +RWStructuredBuffer coneDir; + +[numthreads(1, 1, 1)] +void testBoundingConeAngleAverage(uint3 threadId : SV_DispatchThreadID) { - if (evalRtAlphaTest(attribs)) - IgnoreHit(); + const uint index = threadId.x; + boundBoxSubtendedConeAngleAverage(origin[index], aabbMin[index], aabbMax[index], + coneDir[index], sinTheta[index], cosTheta[index]); } -// What code is executed when we have a new closest hitpoint? -[shader("closesthit")] -void ShadowClosestHit(inout ShadowRayPayload rayData, BuiltInTriangleIntersectionAttributes attribs) +[numthreads(1, 1, 1)] +void testBoundingConeAngleCenter(uint3 threadId : SV_DispatchThreadID) { + const uint index = threadId.x; + boundBoxSubtendedConeAngleCenter(origin[index], aabbMin[index], aabbMax[index], + coneDir[index], sinTheta[index], cosTheta[index]); } diff --git a/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp new file mode 100644 index 000000000..602ab895d --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp @@ -0,0 +1,251 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Utils/Algorithm/ComputeParallelReduction.h" +#include "glm/detail/type_half.hpp" +#include + +namespace Falcor +{ + namespace + { + // Utility classes for compact data types on the GPU. + // TODO: Make types like these part of new math library. + + // Unsigned normalized integer. + template + struct unorm_t + { + unorm_t(float v) { _val = static_cast(std::min(std::max(v, 0.f), 1.f) * _scale + 0.5f); } + unorm_t& operator= (float v) { _val = unorm_t(v); return *this; } + operator float() { return static_cast(_val) / _scale; } + private: + static const uint32_t _scale = (1u << bits) - 1; + T _val; + }; + + using unorm8_t = unorm_t; + using unorm16_t = unorm_t; + + // Signed normalized integer. + template + struct snorm_t + { + snorm_t(float v) { _val = static_cast(std::min(std::max(v, -1.f), 1.f) * _scale + (v >= 0.f ? 0.5f : -0.5f)); } + snorm_t& operator= (float v) { _val = snorm_t(v); return *this; } + operator float() { return static_cast(_val) / _scale; } + private: + static const uint32_t _scale = (1u << (bits - 1)) - 1; + T _val; + }; + + using snorm8_t = snorm_t; + using snorm16_t = snorm_t; + + // Half-precision floating-point number. + struct half + { + half(float v) { _val = glm::detail::toFloat16(v); } + operator float() { return glm::detail::toFloat32(_val); } + private: + glm::detail::hdata _val; + }; + + // Quantize and store value. The de-quantized value is returned in 'val'. + template + void store(void* ptr, size_t idx, float& val) + { + T qval = static_cast(val); + val = static_cast(qval); + reinterpret_cast(ptr)[idx] = qval; + } + + template + void testReduction(GPUUnitTestContext& ctx, const ComputeParallelReduction::SharedPtr& pReduction, ResourceFormat format, uint32_t width, uint32_t height) + { + // Create random test data. + const uint32_t channels = getFormatChannelCount(format); + const size_t elems = width * height * channels; + const uint32_t sz = getFormatBytesPerBlock(format) / channels; + const FormatType type = getFormatType(format); + + assert(getFormatPixelsPerBlock(format) == 1); + assert(!isCompressedFormat(format)); + assert(channels >= 1); + assert(elems > 0); + assert(sz * channels == getFormatBytesPerBlock(format)); + assert(sz == 1 || sz == 2 || sz == 4); + assert(type != FormatType::Unknown && type != FormatType::UnormSrgb); + + std::default_random_engine rng; + auto dist = std::uniform_real_distribution(); + auto u = [&]() -> float { return dist(rng); }; + + auto pInitData = std::make_unique(elems * sz); + void* ptr = pInitData.get(); + + RefType refSum[4] = {}; + RefType absSum[4] = {}; + + for (size_t i = 0; i < elems; i++) + { + // Compute random quantized number. + // The values are in a range that is exactly representable in float. + float value = 0.f; + if (type == FormatType::Float) + { + value = u() * 200.f - 100.f; + if (sz == 2) store(ptr, i, value); + else if (sz == 4) store(ptr, i, value); + else should_not_get_here(); + } + else if (type == FormatType::Sint) + { + value = u() * 200.f - 100.f; + if (sz == 1) store(ptr, i, value); + else if (sz == 2) store(ptr, i, value); + else if (sz == 4) store(ptr, i, value); + else should_not_get_here(); + } + else if (type == FormatType::Uint) + { + value = u() * 200.f; + if (sz == 1) store(ptr, i, value); + else if (sz == 2) store(ptr, i, value); + else if (sz == 4) store(ptr, i, value); + else should_not_get_here(); + } + else if (type == FormatType::Unorm) + { + value = u(); + if (sz == 1) store(ptr, i, value); + else if (sz == 2) store(ptr, i, value); + else should_not_get_here(); + } + else if (type == FormatType::Snorm) + { + value = u() * 2.f - 1.f; + if (sz == 1) store(ptr, i, value); + else if (sz == 2) store(ptr, i, value); + else should_not_get_here(); + } + else should_not_get_here(); + + // Compute reference sum (per channel). + refSum[i % channels] += (RefType)value; + absSum[i % channels] += (RefType)std::abs(value); + } + + // Create a texture with test data. + Texture::SharedPtr pTexture = Texture::create2D(width, height, format, 1, 1, pInitData.get()); + if (!pTexture) throw ErrorRunningTestException("Failed to allocate texture"); + + // Allocate buffer for the result on the GPU. + DataType nullValue = {}; + Buffer::SharedPtr pResultBuffer = Buffer::create(16, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); + + // Perform reduction operation. + DataType result; + bool success = pReduction->execute(ctx.getRenderContext(), pTexture, ComputeParallelReduction::Type::Sum, &result, pResultBuffer, 0); + EXPECT_EQ(success, true); + + // Verify that returned result is identical to result stored to GPU buffer. + DataType* resultBuffer = (DataType*)pResultBuffer->map(Buffer::MapType::Read); + assert(resultBuffer); + for (uint32_t i = 0; i < 4; i++) + { + EXPECT_EQ((*resultBuffer)[i], result[i]) << "i = " << i; + } + pResultBuffer->unmap(); + + // Compare result to reference value computed on the CPU. + for (uint32_t i = 0; i < 4; i++) + { + if (i < channels) + { + if constexpr (std::is_floating_point::value) + { + // For floating-point formats, calculate relative error with respect to the sum of absolute values. + double e = std::abs((RefType)result[i] - refSum[i]); + double relError = (double)e / absSum[i]; + EXPECT_LE(relError, 1e-6) << "i = " << i; + } + else + { + // For integer formats, we expect the exact result. + EXPECT_EQ(result[i], refSum[i]) << "i = " << i; + } + } + else + { + EXPECT_EQ(result[i], 0) << "i = " << i; + } + } + } + + void testReduction(GPUUnitTestContext& ctx, const ComputeParallelReduction::SharedPtr& pReduction, ResourceFormat format, uint32_t width, uint32_t height) + { + const FormatType type = getFormatType(format); + if (type == FormatType::Uint) testReduction(ctx, pReduction, format, width, height); + else if (type == FormatType::Sint) testReduction(ctx, pReduction, format, width, height); + else testReduction(ctx, pReduction, format, width, height); + } + } + + GPU_TEST(ParallelReduction) + { + // Quick test of the snorm/unorm data types we use. + assert((float)unorm8_t(163.499f / 255.f) == (163 / 255.f)); + assert((float)unorm16_t(163.501f / 65535.f) == (164 / 65535.f)); + assert((float)snorm8_t(10.499f / 127.f) == (10 / 127.f)); + assert((float)snorm8_t(10.501f / 127.f) == (11 / 127.f)); + assert((float)snorm8_t(-10.499f / 127.f) == (-10 / 127.f)); + assert((float)snorm8_t(-10.501f / 127.f) == (-11 / 127.f)); + assert((float)snorm16_t(-10.499f / 32767.f) == (-10 / 32767.f)); + assert((float)snorm16_t(-10.501f / 32767.f) == (-11 / 32767.f)); + + // Create reduction operation. + ComputeParallelReduction::SharedPtr pReduction = ComputeParallelReduction::create(); + + // Test floating-point formats. + testReduction(ctx, pReduction, ResourceFormat::RGBA32Float, 1, 1); + testReduction(ctx, pReduction, ResourceFormat::RGBA32Float, 32, 64); + testReduction(ctx, pReduction, ResourceFormat::RGBA32Float, 127, 71); + testReduction(ctx, pReduction, ResourceFormat::RGBA8Unorm, 256, 192); + testReduction(ctx, pReduction, ResourceFormat::RGBA8Snorm, 91, 130); + testReduction(ctx, pReduction, ResourceFormat::RG16Float, 220, 121); + testReduction(ctx, pReduction, ResourceFormat::RG16Unorm, 256, 192); + testReduction(ctx, pReduction, ResourceFormat::RG16Snorm, 333, 101); + + // Test integer formats. + testReduction(ctx, pReduction, ResourceFormat::RGBA32Uint, 33, 99); + testReduction(ctx, pReduction, ResourceFormat::R32Uint, 22, 291); + testReduction(ctx, pReduction, ResourceFormat::R16Int, 64, 33); + testReduction(ctx, pReduction, ResourceFormat::RG8Int, 403, 57); + } +} diff --git a/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp new file mode 100644 index 000000000..eeb7f5ab2 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************************************************************************/ +#include "Testing/UnitTest.h" +#include "Utils/Algorithm/PrefixSum.h" +#include + +namespace Falcor +{ + namespace + { + uint32_t prefixSum(std::vector& elems) + { + // Perform exclusive scan. Return sum of all elements. + uint32_t sum = 0; + for (auto& it : elems) + { + uint32_t tmp = it; + it = sum; + sum += tmp; + } + return sum; + } + + void testPrefixSum(GPUUnitTestContext& ctx, const PrefixSum::SharedPtr& pPrefixSum, uint32_t numElems) + { + // Create a buffer of random data to use as test data. + // We make sure the total sum fits in 32 bits. + assert(numElems > 0); + const uint32_t maxVal = std::numeric_limits::max() / numElems; + std::vector testData(numElems); + std::mt19937 r; + for (auto& it : testData) it = r() % maxVal; + + Buffer::SharedPtr pTestDataBuffer = Buffer::create(numElems * sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, testData.data()); + if (!pTestDataBuffer) throw ErrorRunningTestException("Failed to allocate buffer"); + + // Allocate buffer for the total sum on the GPU. + uint32_t nullValue = 0; + Buffer::SharedPtr pSumBuffer = Buffer::create(4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); + + // Execute prefix sum on the GPU. + uint32_t sum = 0; + bool retval = pPrefixSum->execute(ctx.getRenderContext(), pTestDataBuffer, numElems, &sum, pSumBuffer, 0); + EXPECT_EQ(retval, true); + + // Compute prefix sum on the CPU for comparison. + const uint32_t refSum = prefixSum(testData); + + // Compare results. + EXPECT_EQ(sum, refSum); + + uint32_t* resultSum = (uint32_t*)pSumBuffer->map(Buffer::MapType::Read); + assert(resultSum); + EXPECT_EQ(resultSum[0], refSum); + pSumBuffer->unmap(); + + const uint32_t* result = (const uint32_t*)pTestDataBuffer->map(Buffer::MapType::Read); + assert(result); + for (uint32_t i = 0; i < numElems; i++) + { + EXPECT_EQ(testData[i], result[i]) << "i = " << i; + } + pTestDataBuffer->unmap(); + } + } + + GPU_TEST(PrefixSum) + { + // Quick test of our reference function. + std::vector x({ 5, 17, 2, 9, 23 }); + uint32_t sum = prefixSum(x); + assert(x[0] == 0 && x[1] == 5 && x[2] == 22 && x[3] == 24 && x[4] == 33); + assert(sum == 56); + + // Create helper class. + PrefixSum::SharedPtr pPrefixSum = PrefixSum::create(); + + // Test prefix sums on varying size buffers. + testPrefixSum(ctx, pPrefixSum, 1); + testPrefixSum(ctx, pPrefixSum, 27); + testPrefixSum(ctx, pPrefixSum, 64); + testPrefixSum(ctx, pPrefixSum, 2049); + testPrefixSum(ctx, pPrefixSum, 10201); + testPrefixSum(ctx, pPrefixSum, 231917); + testPrefixSum(ctx, pPrefixSum, 1088921); + } +} diff --git a/Samples/RenderGraph/RenderGraphViewer/Data/DefaultPassIcon.png b/Source/Tools/RenderGraphEditor/Data/DefaultPassIcon.png similarity index 100% rename from Samples/RenderGraph/RenderGraphViewer/Data/DefaultPassIcon.png rename to Source/Tools/RenderGraphEditor/Data/DefaultPassIcon.png diff --git a/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.cpp b/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp similarity index 55% rename from Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.cpp rename to Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp index 370d093d4..da9b5b138 100644 --- a/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.cpp +++ b/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp @@ -26,16 +26,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include "RenderGraphEditor.h" -#include "Experimental/RenderGraph/RenderPassLibrary.h" #include #include #include "RenderGraphEditor.h" namespace fs = std::experimental::filesystem; -const char* kViewerExecutableName = "RenderGraphViewer"; +const char* kViewerExecutableName = "Mogwai"; +const char* kScriptSwitch = "script"; const char* kGraphFileSwitch = "graphFile"; -const char* kGraphNameSwitch = "graphname"; +const char* kGraphNameSwitch = "graphName"; const char* kEditorSwitch = "editor"; const char* kDefaultPassIcon = "DefaultPassIcon.png"; @@ -57,19 +57,16 @@ RenderGraphEditor::~RenderGraphEditor() } } -void RenderGraphEditor::onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) +void RenderGraphEditor::onLoad(RenderContext* pRenderContext) { - const auto& argList = pSample->getArgList(); + const auto& argList = gpFramework->getArgList(); std::string filePath; if (argList.argExists(kGraphFileSwitch)) { filePath = argList[kGraphFileSwitch].asString(); } - mpDefaultIconTex = createTextureFromFile(kDefaultPassIcon, false, false); - pSample->toggleText(false); - pSample->toggleGlobalUI(false); - + mpDefaultIconTex = Texture::createFromFile(kDefaultPassIcon, false, false); loadAllPassLibraries(); if (filePath.size()) @@ -82,153 +79,145 @@ void RenderGraphEditor::onLoad(SampleCallbacks* pSample, RenderContext* pRenderC if (argList.argExists(kEditorSwitch)) mUpdateFilePath = filePath; } - else - { - createNewGraph("DefaultRenderGraph"); - } + else createNewGraph("DefaultRenderGraph"); } -void RenderGraphEditor::onDroppedFile(SampleCallbacks* pCallbacks, const std::string& filename) +void RenderGraphEditor::onDroppedFile(const std::string& filename) { std::string ext = getExtensionFromFile(filename); if (ext == "dll") RenderPassLibrary::instance().loadLibrary(filename); else if (ext == "py") { if (mViewerRunning) { msgBox("Viewer is running. Please close the viewer before loading a graph file.", MsgBoxType::Ok); } - else - { - loadGraphsFromFile(filename); - mpGraphs[mCurrentGraphIndex]->onResize(pCallbacks->getCurrentFbo().get()); - } + else loadGraphsFromFile(filename); } } // some of this will need to be moved to render graph ui -void RenderGraphEditor::onGuiRender(SampleCallbacks* pSample, Gui* pGui) +void RenderGraphEditor::onGuiRender(Gui* pGui) { + RenderContext* pRenderContext = gpFramework->getRenderContext(); + uint32_t screenHeight = mWindowSize.y; uint32_t screenWidth = mWindowSize.x; - if (pGui->beginMainMenuBar()) + Gui::MainMenu menu(pGui); + Gui::Menu::Dropdown fileMenu = menu.dropdown("File"); + if (!mShowCreateGraphWindow && fileMenu.item("Create New Graph")) { - if (pGui->beginDropDownMenu("File")) - { - if (!mShowCreateGraphWindow && pGui->addMenuItem("Create New Graph")) - { - mShowCreateGraphWindow = true; - } - - if (pGui->addMenuItem("Load File")) - { - std::string renderGraphFilePath; - if (mViewerRunning) - { - msgBox("Viewer is running. Please close the viewer before loading a graph file.", MsgBoxType::Ok); - } - else - { - if (openFileDialog({}, renderGraphFilePath)) - { - loadGraphsFromFile(renderGraphFilePath); - mpGraphs[mCurrentGraphIndex]->onResize(pSample->getCurrentFbo().get()); - } - } - } + mShowCreateGraphWindow = true; + } - if (pGui->addMenuItem("Save To File")) - { - bool saveGraph = true; - std::string log; - - if (!mpGraphs[mCurrentGraphIndex]->isValid(log)) - { - MsgBoxButton msgBoxButton = msgBox("Attempting to save invalid graph.\nGraph may not execute correctly when loaded\nAre you sure you want to save the graph?" - , MsgBoxType::OkCancel); - saveGraph = !(msgBoxButton == MsgBoxButton::Cancel); - } - - if (saveGraph) - { - std::string renderGraphFileName = mOpenGraphNames[mCurrentGraphIndex].label + ".py"; - if (saveFileDialog(RenderGraph::kFileExtensionFilters, renderGraphFileName)) serializeRenderGraph(renderGraphFileName); - } - } + if (fileMenu.item("Load File")) + { + std::string renderGraphFilePath; + if (mViewerRunning) + { + msgBox("Viewer is running. Please close the viewer before loading a graph file.", MsgBoxType::Ok); + } + else + { + if (openFileDialog({}, renderGraphFilePath)) loadGraphsFromFile(renderGraphFilePath); + } + } - if (pGui->addMenuItem("Load Pass Library")) - { - std::string passLib; - FileDialogFilterVec filters = { {"dll"} }; - if(openFileDialog(filters, passLib)) - { - RenderPassLibrary::instance().loadLibrary(passLib); - } - } + if (fileMenu.item("Save To File")) + { + bool saveGraph = true; + std::string log; - pGui->endDropDownMenu(); + try + { + std::string s; + mpGraphs[mCurrentGraphIndex]->compile(pRenderContext, s); + } + catch (std::exception e) + { + MsgBoxButton msgBoxButton = msgBox("Attempting to save invalid graph.\nGraph may not execute correctly when loaded\nAre you sure you want to save the graph?" + , MsgBoxType::OkCancel); + saveGraph = !(msgBoxButton == MsgBoxButton::Cancel); } - if (pGui->beginDropDownMenu("Window")) + if (saveGraph) { - pGui->addMenuItem("Debug Window", mShowDebugWindow); - pGui->endDropDownMenu(); + std::string renderGraphFileName = mOpenGraphNames[mCurrentGraphIndex].label + ".py"; + if (saveFileDialog(RenderGraph::kFileExtensionFilters, renderGraphFileName)) serializeRenderGraph(renderGraphFileName); } + } - pGui->endMainMenuBar(); + if (fileMenu.item("Load Pass Library")) + { + std::string passLib; + FileDialogFilterVec filters = { {"dll"} }; + if (openFileDialog(filters, passLib)) + { + RenderPassLibrary::instance().loadLibrary(passLib); + } } + fileMenu.release(); + + Gui::Menu::Dropdown windowMenu = menu.dropdown("Window"); + windowMenu.item("Debug Window", mShowDebugWindow); + windowMenu.release(); + + menu.release(); // sub window for listing available window passes - pGui->pushWindow("Render Passes", screenWidth * 3 / 5, screenHeight / 4 - 20, screenWidth / 5, screenHeight * 3 / 4 + 20, true); + Gui::Window passWindow(pGui, "Render Passes", { screenWidth * 3 / 5, screenHeight / 4 - 20 }, { screenWidth / 5, screenHeight * 3 / 4 + 20 }); if (mResetGuiWindows) { - pGui->setCurrentWindowSize(screenWidth * 3 / 5, screenHeight / 4 - 20); - pGui->setCurrentWindowPos(screenWidth / 5, screenHeight * 3 / 4 + 20); + passWindow.windowSize(screenWidth * 3 / 5, screenHeight / 4 - 20); + passWindow.windowPos(screenWidth / 5, screenHeight * 3 / 4 + 20); } - pGui->beginColumns(5); + passWindow.columns(5); auto renderPasses = RenderPassLibrary::instance().enumerateClasses(); - for (size_t i = 0 ; i < renderPasses.size() ; i++) + for (size_t i = 0; i < renderPasses.size(); i++) { const auto& pass = renderPasses[i]; - pGui->addRect({ 148.0f, 64.0f }, pGui->pickUniqueColor(pass.className), false); - pGui->addImage((std::string("RenderPass##") + std::to_string(i)).c_str(), mpDefaultIconTex, { 148.0f, 44.0f }); - pGui->dragDropSource(pass.className, "RenderPassType", pass.className); - pGui->addText(pass.className); - pGui->addTooltip(pass.desc, true); - pGui->nextColumn(); + passWindow.rect({ 148.0f, 64.0f }, pGui->pickUniqueColor(pass.className), false); + passWindow.image((std::string("RenderPass##") + std::to_string(i)).c_str(), mpDefaultIconTex, { 148.0f, 44.0f }); + passWindow.dragDropSource(pass.className, "RenderPassType", pass.className); + passWindow.text(pass.className); + passWindow.tooltip(pass.desc, true); + passWindow.nextColumn(); } - pGui->popWindow(); + passWindow.release(); - // diff name for this? - // create window for render ui. - pGui->pushWindow("Render UI", screenWidth * 1 / 5, screenHeight - 20, screenWidth * 4 / 5, 20, true); - pGui->popWindow(); + Gui::Window renderWindow(pGui, "Render UI", { screenWidth * 1 / 5, screenHeight - 20 }, { screenWidth * 4 / 5, 20 }); + if (mResetGuiWindows) + { + renderWindow.windowSize(screenWidth * 1 / 5, screenHeight - 20); + renderWindow.windowPos(screenWidth * 4 / 5, 20); + } + renderWindow.release(); // push a sub GUI window for the node editor - pGui->pushWindow("Graph Editor", screenWidth * 4 / 5, screenHeight * 3 / 4, 0, 20, false); + Gui::Window editorWindow(pGui, "Graph Editor", { screenWidth * 4 / 5, screenHeight * 3 / 4 }, { 0, 20 }, Gui::WindowFlags::SetFocus | Gui::WindowFlags::AllowMove); if (mResetGuiWindows) { - pGui->setCurrentWindowSize(screenWidth * 4 / 5, screenHeight * 3 / 4); - pGui->setCurrentWindowPos(0, 20); + editorWindow.windowSize(screenWidth * 4 / 5, screenHeight * 3 / 4); + editorWindow.windowPos(0, 20); } - mRenderGraphUIs[mCurrentGraphIndex].renderUI(pGui); - pGui->popWindow(); + mRenderGraphUIs[mCurrentGraphIndex].renderUI(pRenderContext, pGui); + editorWindow.release(); for (auto& renderGraphUI : mRenderGraphUIs) { mCurrentLog += renderGraphUI.getCurrentLog(); renderGraphUI.clearCurrentLog(); } - - pGui->pushWindow("Graph Editor Settings", screenWidth / 5, screenHeight / 4 - 20, 0, screenHeight * 3 / 4 + 20, true); + + Gui::Window settingsWindow(pGui, "Graph Editor Settings", { screenWidth / 5, screenHeight / 4 - 20 }, { 0, screenHeight * 3 / 4 + 20 }); if (mResetGuiWindows) { - pGui->setCurrentWindowSize(screenWidth / 5, screenHeight / 4 - 20); - pGui->setCurrentWindowPos(0, screenHeight * 3 / 4 + 20); + settingsWindow.windowSize(screenWidth / 5, screenHeight / 4 - 20); + settingsWindow.windowPos(0, screenHeight * 3 / 4 + 20); } uint32_t selection = static_cast(mCurrentGraphIndex); - if (mOpenGraphNames.size() && pGui->addDropdown("Open Graph", mOpenGraphNames, selection)) + if (mOpenGraphNames.size() && settingsWindow.dropdown("Open Graph", mOpenGraphNames, selection)) { // Display graph mCurrentGraphIndex = selection; @@ -236,7 +225,7 @@ void RenderGraphEditor::onGuiRender(SampleCallbacks* pSample, Gui* pGui) if (mUpdateFilePath.size()) { - mRenderGraphUIs[mCurrentGraphIndex].writeUpdateScriptToFile(mUpdateFilePath, pSample->getLastFrameTime()); + mRenderGraphUIs[mCurrentGraphIndex].writeUpdateScriptToFile(pRenderContext, mUpdateFilePath, (float)gpFramework->getFrameRate().getLastFrameTime()); } if (mViewerRunning && mViewerProcess) @@ -249,18 +238,25 @@ void RenderGraphEditor::onGuiRender(SampleCallbacks* pSample, Gui* pGui) mUpdateFilePath.clear(); } } - + // validate the graph and output the current status to the console - if (pGui->addButton("Validate Graph")) + if (settingsWindow.button("Validate Graph")) { - std::string currentLog; - bool valid = mpGraphs[mCurrentGraphIndex]->isValid(currentLog); - std::string log = (valid ? "Graph is Valid\n" : "Graph is currently invalid.\n"); - msgBox((log + currentLog).c_str()); - mCurrentLog += log + currentLog; + std::string s; + try + { + mpGraphs[mCurrentGraphIndex]->compile(pRenderContext, s); + s = "The graph is valid"; + } + catch (std::exception e) + { + s = std::string("The graph is invalid. ") + e.what(); + } + msgBox(s); + mCurrentLog += s; } - if (pGui->addButton("Auto-Generate Edges")) + if (settingsWindow.button("Auto-Generate Edges")) { std::vector executionOrder = mRenderGraphUIs[mCurrentGraphIndex].getPassOrder(); mpGraphs[mCurrentGraphIndex]->autoGenEdges(executionOrder); @@ -268,30 +264,13 @@ void RenderGraphEditor::onGuiRender(SampleCallbacks* pSample, Gui* pGui) } auto pScene = mpGraphs[mCurrentGraphIndex]->getScene(); - if (pScene) - { - pGui->addText( (std::string("Graph Sets Scene: ") + pScene->getFilename()).c_str()); - } - - if (pGui->addButton("Set Scene")) - { - // display warning when setting scene so that there is no confusion for overwriting default scene - MsgBoxButton setSceneMsg = msgBox("Note: Setting scene in graph will overwrite default scene from viewer."); - if (setSceneMsg == MsgBoxButton::Ok) - { - std::string filename; - if (openFileDialog(Scene::kFileExtensionFilters, filename)) - { - filename = stripDataDirectories(filename); - auto pDummyScene = Scene::create(filename); - mpGraphs[mCurrentGraphIndex]->setScene(pDummyScene); - mSceneSet = true; - } - } - } +// if (pScene) +// { +// settingsWindow.text((std::string("Graph Sets Scene: ") + pScene->getFilename()).c_str()); +// } - std::vector graphOutputString{mGraphOutputEditString}; - if (pGui->addMultiTextBox("Add Output", {"GraphOutput"}, graphOutputString)) + std::vector graphOutputString{ mGraphOutputEditString }; + if (settingsWindow.multiTextbox("Add Output", { "GraphOutput" }, graphOutputString)) { if (mCurrentGraphOutput != mGraphOutputEditString) { @@ -307,67 +286,69 @@ void RenderGraphEditor::onGuiRender(SampleCallbacks* pSample, Gui* pGui) mGraphOutputEditString = graphOutputString[0]; mRenderGraphUIs[mCurrentGraphIndex].setRecordUpdates(mViewerRunning); - if (!mViewerRunning && pGui->addButton("Open Graph Viewer")) + if (!mViewerRunning && settingsWindow.button("Open in Mogwai")) { std::string log; bool openViewer = true; - if (!mpGraphs[mCurrentGraphIndex]->isValid(log)) + try { - openViewer = msgBox("Graph is invalid :\n " + log + "\n Are you sure you want to attempt preview?", MsgBoxType::OkCancel) == MsgBoxButton::Ok; + std::string s; + mpGraphs[mCurrentGraphIndex]->compile(pRenderContext, s); + } + catch (std::exception e) + { + openViewer = msgBox(std::string("Graph is invalid :\n ") + e.what() + "\n Are you sure you want to attempt preview?", MsgBoxType::OkCancel) == MsgBoxButton::Ok; } if (openViewer) { mUpdateFilePath = getTempFilename(); - RenderGraphExporter::save(mpGraphs[mCurrentGraphIndex], mRenderGraphUIs[mCurrentGraphIndex].getName(), mUpdateFilePath, {}, static_cast(mSceneSet)); - + RenderGraphExporter::save(mpGraphs[mCurrentGraphIndex], mUpdateFilePath); + // load application for the editor given it the name of the mapped file - std::string commandLineArgs = "-" + std::string(kEditorSwitch) + " -" + std::string(kGraphFileSwitch) + ' ' + mUpdateFilePath; - commandLineArgs += " -" + std::string(kGraphNameSwitch) + ' ' + std::string(mOpenGraphNames[mCurrentGraphIndex].label); + std::string commandLineArgs = "-" + std::string(kEditorSwitch) + " -" + std::string(kScriptSwitch) + ' ' + mUpdateFilePath; mViewerProcess = executeProcess(kViewerExecutableName, commandLineArgs); assert(mViewerProcess); mViewerRunning = true; } } - pGui->popWindow(); + settingsWindow.release(); if (mShowDebugWindow) { - pGui->pushWindow("output", screenWidth / 4, screenHeight / 4 - 20, screenWidth * 3 / 4, screenHeight * 3 / 4 + 20, true); + Gui::Window debugWindow(pGui, "output", { screenWidth / 4, screenHeight / 4 - 20 }, { screenWidth * 3 / 4, screenHeight * 3 / 4 + 20 }); if (mResetGuiWindows) { - pGui->setCurrentWindowSize(screenWidth / 4, screenHeight / 4 - 20); - pGui->setCurrentWindowPos(screenWidth * 3 / 4, screenHeight * 3 / 4 + 20); + debugWindow.windowSize(screenWidth / 4, screenHeight / 4 - 20); + debugWindow.windowPos(screenWidth * 3 / 4, screenHeight * 3 / 4 + 20); } - renderLogWindow(pGui); - pGui->popWindow(); + renderLogWindow(debugWindow); + debugWindow.release(); } - + // pop up window for naming a new render graph if (mShowCreateGraphWindow) { - pGui->pushWindow("CreateNewGraph", 256, 128, screenWidth / 2 - 128, screenHeight / 2 - 64); + Gui::Window createWindow(pGui, "CreateNewGraph", { 256, 128 }, { screenWidth / 2 - 128, screenHeight / 2 - 64 }); + createWindow.textbox("Graph Name", mNextGraphString); - pGui->addTextBox("Graph Name", mNextGraphString); - - if (pGui->addButton("Create Graph") && mNextGraphString[0]) + if (createWindow.button("Create Graph") && mNextGraphString[0]) { createNewGraph(mNextGraphString); - mpGraphs[mCurrentGraphIndex]->onResize(pSample->getCurrentFbo().get()); mNextGraphString.clear(); mNextGraphString.resize(255, 0); mShowCreateGraphWindow = false; } - if (pGui->addButton("Cancel", true)) + if (createWindow.button("Cancel", true)) { mNextGraphString.clear(); mNextGraphString.resize(255, 0); mShowCreateGraphWindow = false; } - pGui->popWindow(); + createWindow.release(); } mResetGuiWindows = false; @@ -381,12 +362,12 @@ void RenderGraphEditor::loadAllPassLibraries() for (auto& file : fs::directory_iterator(executableDirectory)) { std::string filename = file.path().string(); - if (getExtensionFromFile(filename) == ".dll") + if (getExtensionFromFile(filename) == "dll") { // check for addPasses() std::string fullpath; findFileInDataDirectories(filename, fullpath); - DllHandle l = loadDll((fullpath + ".falcor").c_str()); + DllHandle l = loadDll(fullpath.c_str()); auto pGetPass = (RenderPassLibrary::LibraryFunc)getDllProcAddress(l, "getPasses"); if (pGetPass) @@ -398,15 +379,15 @@ void RenderGraphEditor::loadAllPassLibraries() } } -void RenderGraphEditor::renderLogWindow(Gui* pGui) +void RenderGraphEditor::renderLogWindow(Gui::Widgets& widget) { // window for displaying log from render graph validation - pGui->addText(mCurrentLog.c_str()); + widget.text(mCurrentLog.c_str()); } void RenderGraphEditor::serializeRenderGraph(const std::string& fileName) { - RenderGraphExporter::save(mpGraphs[mCurrentGraphIndex], mRenderGraphUIs[mCurrentGraphIndex].getName(), fileName, {}); + RenderGraphExporter::save(mpGraphs[mCurrentGraphIndex], fileName); } void RenderGraphEditor::deserializeRenderGraph(const std::string& fileName) @@ -423,30 +404,28 @@ void RenderGraphEditor::loadGraphsFromFile(const std::string& fileName, const st assert(fileName.size()); // behavior is load each graph defined within the file as a separate editor ui - std::vector newGraphs; + std::vector newGraphs; if (graphName.size()) { auto pGraph = RenderGraphImporter::import(graphName, fileName); - if (pGraph) newGraphs.push_back({ graphName, pGraph}); + if (pGraph) newGraphs.push_back(pGraph); } else { newGraphs = RenderGraphImporter::importAllGraphs(fileName); } - for (const auto& graphInfo : newGraphs) + for (const auto& pGraph : newGraphs) { - const std::string& name = graphInfo.name; - const RenderGraph::SharedPtr& newGraph = graphInfo.pGraph; - + const std::string& name = pGraph->getName(); auto nameToIndexIt = mGraphNamesToIndex.find(name); if (nameToIndexIt != mGraphNamesToIndex.end()) { - MsgBoxButton button = msgBox("Warning! Graph is already open. Update graph from file?", MsgBoxType::OkCancel); - if (button == MsgBoxButton::Ok) + MsgBoxButton button = msgBox("Warning! Graph is already open. Update graph from file?", MsgBoxType::YesNo); + if (button == MsgBoxButton::Yes) { mCurrentGraphIndex = nameToIndexIt->second; - mpGraphs[mCurrentGraphIndex]->update(newGraph); + mpGraphs[mCurrentGraphIndex]->update(pGraph); mRenderGraphUIs[mCurrentGraphIndex].reset(); continue; } @@ -454,7 +433,7 @@ void RenderGraphEditor::loadGraphsFromFile(const std::string& fileName, const st else { mCurrentGraphIndex = mpGraphs.size(); - mpGraphs.push_back(newGraph); + mpGraphs.push_back(pGraph); mRenderGraphUIs.push_back(RenderGraphUI(mpGraphs[mCurrentGraphIndex], name)); Gui::DropdownValue nextGraphID; @@ -479,6 +458,7 @@ void RenderGraphEditor::createNewGraph(const std::string& renderGraphName) } // Matt TODO can we put the GUI dropdown code in a shared function shared with 'loadFromFile'? graphName = tempGraphName; + newGraph->setName(graphName); mCurrentGraphIndex = mpGraphs.size(); mpGraphs.push_back(newGraph); mRenderGraphUIs.push_back(RenderGraphUI(newGraph, graphName)); @@ -490,17 +470,16 @@ void RenderGraphEditor::createNewGraph(const std::string& renderGraphName) mOpenGraphNames.push_back(nextGraphID); } -void RenderGraphEditor::onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) +void RenderGraphEditor::onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) { const glm::vec4 clearColor(0.25, 0.25, 0.25 , 1); pRenderContext->clearFbo(pTargetFbo.get(), clearColor, 1.0f, 0, FboAttachmentType::All); - pSample->getRenderContext()->getGraphicsState()->setFbo(pTargetFbo); - mRenderGraphUIs[mCurrentGraphIndex].updateGraph(); + mRenderGraphUIs[mCurrentGraphIndex].updateGraph(pRenderContext); } -void RenderGraphEditor::onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) +void RenderGraphEditor::onResizeSwapChain(uint32_t width, uint32_t height) { - mpGraphs[mCurrentGraphIndex]->onResize(pSample->getCurrentFbo().get()); + for(auto pG : mpGraphs) pG->onResize(gpFramework->getTargetFbo().get()); mWindowSize = {width, height}; mResetGuiWindows = true; } diff --git a/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.h b/Source/Tools/RenderGraphEditor/RenderGraphEditor.h similarity index 82% rename from Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.h rename to Source/Tools/RenderGraphEditor/RenderGraphEditor.h index d4fab52d2..f14be73a7 100644 --- a/Samples/RenderGraph/RenderGraphEditor/RenderGraphEditor.h +++ b/Source/Tools/RenderGraphEditor/RenderGraphEditor.h @@ -27,18 +27,17 @@ ***************************************************************************/ #pragma once #include "Falcor.h" -#include "FalcorExperimental.h" using namespace Falcor; -class RenderGraphEditor : public Renderer +class RenderGraphEditor : public IRenderer { public: - void onLoad(SampleCallbacks* pSample, RenderContext* pRenderContext) override; - void onFrameRender(SampleCallbacks* pSample, RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; - void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override; - void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override; - void onDroppedFile(SampleCallbacks* pCallbacks, const std::string& filename) override; + void onLoad(RenderContext* pRenderContext) override; + void onFrameRender(RenderContext* pRenderContext, const Fbo::SharedPtr& pTargetFbo) override; + void onResizeSwapChain(uint32_t width, uint32_t height) override; + void onGuiRender(Gui* pGui) override; + void onDroppedFile(const std::string& filename) override; RenderGraphEditor(); ~RenderGraphEditor(); @@ -48,7 +47,7 @@ class RenderGraphEditor : public Renderer void loadGraphsFromFile(const std::string& fileName, const std::string& graphName = ""); void serializeRenderGraph(const std::string& fileName); void deserializeRenderGraph(const std::string& fileName); - void renderLogWindow(Gui* pGui); + void renderLogWindow(Gui::Widgets& widget); void loadAllPassLibraries(); std::vector mpGraphs; @@ -62,7 +61,6 @@ class RenderGraphEditor : public Renderer std::string mGraphOutputEditString; std::string mUpdateFilePath; Texture::SharedPtr mpDefaultIconTex; - bool mSceneSet = false; Gui::DropdownList mOpenGraphNames; bool mShowCreateGraphWindow = false; diff --git a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj b/Source/Tools/RenderGraphEditor/RenderGraphEditor.vcxproj similarity index 65% rename from Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj rename to Source/Tools/RenderGraphEditor/RenderGraphEditor.vcxproj index adec0fe34..f5f3868f9 100644 --- a/Samples/Effects/SkyBoxRenderer/SkyBoxRenderer.vcxproj +++ b/Source/Tools/RenderGraphEditor/RenderGraphEditor.vcxproj @@ -1,4 +1,4 @@ - + @@ -11,83 +11,77 @@ - + - + - - {3b602f0e-3834-4f73-b97d-7dfc91597a98} + + {2c535635-e4c5-4098-a928-574f0e7cd5f9} - {0C3483E0-B6C1-41BC-B8F9-306F9BA5F287} - Win32Proj - EnvMap + 15.0 + {DE81ACAA-933F-4DBC-A7EB-D69B9CB0BA71} + RenderGraphEditor 10.0.17763.0 - SkyBoxRenderer Application true v141 - Unicode + MultiByte Application false v141 true - Unicode + MultiByte + + - + + - + + - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - - - Windows - true - - + Level3 - - MaxSpeed true true - WIN32;NDEBUG;%(PreprocessorDefinitions) + true + true + stdcpp17 + true - Windows - true true true + + + Level3 + Disabled + true + WIN32;_DEBUG;%(PreprocessorDefinitions) + stdcpp17 + true + + diff --git a/Tests/BuildSolution.bat b/Tests/BuildSolution.bat index 9dabba8ba..af93c9423 100644 --- a/Tests/BuildSolution.bat +++ b/Tests/BuildSolution.bat @@ -30,14 +30,16 @@ for /f "usebackq tokens=*" %%i in (`"%VS_WHERE%" -latest -property installationP set solution=%2 set project=%4 set errFileSuffix=_BuildLog.txt +set CL=%CL% /D_TEST_ if defined project ( set errFile="%project%%errFileSuffix%" - echo Starting %action% of %config% config of project %project% in solution %2 + echo %action%ing the %config% config of project %project% in solution %2 call "%VS_INSTALL_DIR%\Common7\IDE\devenv.com" %solution% /%action% %config% /project %project% > !errFile! + call type !errFile! ) else ( set errFile="Solution%errFileSuffix%" - echo Starting %action% of %config% config of entire solution %2 - call "%VS_INSTALL_DIR%\Common7\IDE\devenv.com" %solution% /%action% %config% > !errFile! + echo %action%ing %config% config of solution %2 + call "%VS_INSTALL_DIR%\Common7\IDE\devenv.com" %solution% /%action% %config% > !errFile! 2>&1 ) if not %errorlevel%==0 ( goto buildFailed @@ -49,6 +51,7 @@ exit /B 0 :buildFailed echo Build Failed +call type !errFile! exit /B 1 :cantFindVs diff --git a/Tests/CompareOutput.py b/Tests/CompareOutput.py index c17c64d39..7ce0c1940 100644 --- a/Tests/CompareOutput.py +++ b/Tests/CompareOutput.py @@ -1,19 +1,22 @@ import os import subprocess - +import argparse import Helpers as helpers -import InternalConfig as iConfig +import TestConfig as config +from pathlib import Path + +def default_comparison(result_image, reference_image): -def default_comparison(result_image, result_image_dir, reference_image, screen_captures_results): + result_image = os.path.abspath(result_image) + reference_image = os.path.abspath(reference_image) + results_dir = os.path.dirname(result_image) # Create the test compare image. - test_compare_image_filepath = os.path.join(result_image_dir, os.path.splitext(os.path.basename(result_image))[0] + '_Compare.png') + test_compare_image_filepath = os.path.join(results_dir, os.path.splitext(os.path.basename(result_image))[0] + '_Compare.png') # Run ImageMagick image_compare_command = ['magick', 'compare', '-metric', 'MSE', '-compose', 'Src', '-highlight-color', 'White', '-lowlight-color', 'Black', result_image, reference_image, test_compare_image_filepath] - print('Comparison Test: Source Image: ' + result_image + '\nReference Image: ' + reference_image + '\nOutput Image: ' + test_compare_image_filepath + '\n') - if os.name == 'nt': image_compare_process = subprocess.Popen(image_compare_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) else: @@ -28,85 +31,87 @@ def default_comparison(result_image, result_image_dir, reference_image, screen_c image_compare_result = image_compare_result.decode('ascii') except AttributeError: pass - - # Keep the Return Code and the Result. - result = {} - + + success = True + # Image compare succeeded if image_compare_process.returncode <= 1: # 0: Success, 1: Does not match, 2: File not found, or other error? - result_str = image_compare_result[:image_compare_result.find(' ')] - result['Compare Result'] = result_str - result['Test Passed'] = float(result_str) <= iConfig.TestConfig['Tolerance'] - - # if result['Test Passed'] == False: - # print('[FAILED] Comparision above tolerance. Difference was ' + result_str + ' on image ' + result_image) - - # Error + result_str = image_compare_result[(image_compare_result.find('(') + 1):image_compare_result.find(')')] + success = float(result_str) <= config.tolerance else: print('[FAILED] No output file produced. ') - result['Compare Result'] = "Error" - result['Test Passed'] = False - - result['Return Code'] = image_compare_process.returncode - result['Source Filename'] = os.path.abspath(result_image) - result['Reference Filename'] = reference_image - result['Comparison Filename'] = os.path.abspath(test_compare_image_filepath) - - screen_captures_results['Success'] &= result['Test Passed'] - if not result_image_dir in screen_captures_results.keys(): - screen_captures_results[result_image_dir] = [] - screen_captures_results[result_image_dir].append(result) - - return result['Test Passed'] + success = False + if success: + os.remove(test_compare_image_filepath) -# default image comparison -def default_compare(result_file, result_file_dir, reference_file, data): - result = default_comparison(result_file, result_file_dir, reference_file, data) - return result; - + return success -def compare_all_images(results_dir, reference_dir, comparison_func): - screen_captures_results = {} - screen_captures_results['Success'] = True - - num_tests = len(iConfig.TestConfig['Frames']) - - # make sure that expected references exist for the run tests +def get_files_to_compare(subdir, file, basedir, otherbase): + file = os.path.join(subdir, file) + rel_path = Path(file).relative_to(basedir) + return file, os.path.join(otherbase, rel_path) + + +def verify_that_refs_exist(results_dir, reference_dir): + success = True for subdir, dirs, files in os.walk(results_dir): for file in files: if not helpers.isSupportedImageExt(file): - # .html or .txt files from the previous should already be deleted here - print('[FAILED] Unsupported reference file type from: ' + file) continue - - result_file = os.path.join( subdir, file) - relative_file_path = result_file[len(results_dir) + 1 : len(result_file)] - reference_file = os.path.join(reference_dir, relative_file_path) - - if (not os.path.exists(reference_file)): - print('Error: Expecting reference' + str(reference_file) + '. refererence file is missing! \n') + result_file, ref_file = get_files_to_compare(subdir, file, results_dir, reference_dir) + + if (not os.path.exists(ref_file)): + print('Error: Output file "' + str(result_file) + '" doesn\'t have a reference') + success = False + + return success + +def compare(results_dir, reference_dir): + if not os.path.isdir(reference_dir): + print("Can't find reference folder " + reference_dir) + return False + + success = verify_that_refs_exist(results_dir, reference_dir); + + # make sure that expected references exist for the run tests for subdir, dirs, files in os.walk(reference_dir): for file in files: if not helpers.isSupportedImageExt(file): - print('[FAILED] Unsupported reference file type from: ' + file) continue - reference_file = os.path.join( subdir, file) - - # checks that results and references have same number of images - # make sure there is a subsequent file in results_dir - relative_file_path = reference_file[len(reference_dir) + 1 : len(reference_file)] - results_file = os.path.join(results_dir, relative_file_path) - if (not os.path.exists(results_file)): - print('Result file: ' + results_file + ' does not exist') - status = comparison_func(results_file, os.path.split(results_file)[0], reference_file, screen_captures_results) + reference_file, result_file = get_files_to_compare(subdir, file, reference_dir, results_dir) + if (not os.path.exists(result_file)): + print('Error: Expecting output file "' + str(result_file) + '", but it is missing') + success = False + continue + + status = default_comparison(result_file, reference_file) if not status: # add early out option? - print('Error: Test failed on comparison between ' + results_file + ' and reference ' + reference_file) - - - return screen_captures_results + print('Error: Test failed on comparison between ' + result_file + ' and reference ' + reference_file) + success = False + + return success + +def main(): + # Argument Parser. + parser = argparse.ArgumentParser() - \ No newline at end of file + # Add argument for testing directory for render pass tests + parser.add_argument('-rf', '--ref', action='store', help='Specify the reference directory', required=True) + + # Add argument for testing directory for render pass tests + parser.add_argument('-r', '--res', action='store', help='Specify the results directory', required=True) + + # Parse the Arguments. + args = parser.parse_args() + results = compare(args.res, args.ref) + if results['Success']: + print("Test Passed") + else: + print("Test Failed") + +if __name__ == '__main__': + main() diff --git a/Tests/GraphTests.py b/Tests/GraphTests.py new file mode 100644 index 000000000..670406240 --- /dev/null +++ b/Tests/GraphTests.py @@ -0,0 +1,130 @@ +import subprocess +import shutil +from pathlib import Path +import argparse +import CompareOutput as compareOutput +import TestConfig as testConfig +import Helpers as helpers +import os + +def getMogwaiExe(config): + return helpers.findExecutable(config, testConfig.mogwaiExe) + +def compareDirs(outDir, refDir): + if(refDir): + return compareOutput.compare(str(outDir), str(refDir)) + return True + +def writeFile(filename, data): + file = open(filename, "w") + file.write(data) + file.close() + +def generate_config_file(graphFile, outputDir, basename, scene): + a = 'm.loadScene(r"' + str(scene) + '")\n' + a += 'm.script(r"' + str(graphFile) + '")\n' + a += 'm.ui(false)\n' + a += 't.framerate(' + str(testConfig.framerate) + ')\n' + a += 't.now(0)\n' + a += 't.exitTime(' + str(testConfig.exitTime) + ')\n' + a += 'fc.outputDir(r"' + str(outputDir) + '")\n' + a += 'fc.baseFilename("' + str(basename) + '")\n' + a += 'm.activeGraph().markOutput("*")\n' + a += 'fc.frames(m.activeGraph(), ' + str(testConfig.frames) + ')\n' + return a + +def test_graph_with_scene(graphFile, outputDir, scene, buildConfig): + graphFile = Path(graphFile).resolve() + outputDir = Path(outputDir).resolve() + scene = Path(scene) + graphName = graphFile.stem + sceneName = scene.stem + basename = graphName + '.' + sceneName + configFile = outputDir / (basename + ".config.py") + s = generate_config_file(graphFile, outputDir, basename, scene) + writeFile(configFile, s) + helpers.runProcessAsync([getMogwaiExe(buildConfig), '-script', str(configFile)]) + + +def test_graph(graphFile, outputDir, buildConfig, referenceDir): + helpers.cleanDir(outputDir) + for s in testConfig.scenes: + test_graph_with_scene(graphFile, outputDir, s, buildConfig) + return compareDirs(outputDir, referenceDir) + +def print_all_graphs_msg(rootDir, dirFilter): + msg = "Procesing folder " + rootDir + if dirFilter: + msg += " using the directory filter `" + dirFilter + "`\n" + else: + msg += ". No directory filter specified\n" + print(msg) + + +def run_test_if_graph_file(file, dirName, outputDir, buildConfig, refDir): + success = True + file = Path(file) + if(file.suffix == '.py'): + subdir = os.path.splitext(file)[0] + graphOutDir = Path(outputDir) / subdir + file = Path(dirName) / file + print("Testing " + str(file)) + if refDir: + refDir = Path(refDir) / subdir + success = test_graph(file, graphOutDir, buildConfig, refDir) and success + return success + +def test_all_graphs(rootDir, outputDir, dirFilter, buildConfig, refDir): + success = True + dirFilter = dirFilter.lower() + rootDir = os.path.abspath(rootDir) + print_all_graphs_msg(rootDir, dirFilter) + + for dirName, subDirs, files in os.walk(rootDir): + if dirFilter and (Path(dirName).name.lower() != dirFilter): + continue + + for f in files: + success = run_test_if_graph_file(f, dirName, outputDir, buildConfig, refDir) and success + return success + + +def getBuildConfig(args): + if args.config: + return args.config + else: + return testConfig.defaultConfig + +def main(): + # Argument Parser. + parser = argparse.ArgumentParser() + parser.add_argument('--graphsDir', action='store', help='Test all graph files found in a direcoty called `Testing`, which is a specific directory and its subfolders') + parser.add_argument('--graphsDirFilter', action='store', help='Only run graphs found if the subfolder name matches this') + parser.add_argument('-g', '--graphFile', action='store', help='Specify the graph file to test') + parser.add_argument('-o', '--outputDir', action='store', help='The output directory for the output images', required=True) + parser.add_argument('-r', '--reference', action='store', help='Reference images directory. If this arg is provided, the generated results will be compared against the reference.') + parser.add_argument('--config', action='store', help='Build configuration') + args = parser.parse_args() + + if (not args.graphsDir and not args.graphFile) or (args.graphsDir and args.graphFile): + print ("Please specify either '--graphFile' or '--graphFile' within the script arguments. They are mutually exclusive, so don't use both") + sys.exit(1) + + outputDir = Path(args.outputDir) + buildConfig = getBuildConfig(args) + + success = True + + if(args.graphFile): + success = test_graph(args.graphFile, outputDir, buildConfig, args.reference) + + if(args.graphsDir): + success = test_all_graphs(args.graphsDir, outputDir, args.graphsDirFilter, buildConfig, args.reference) + + if success: + print("Graph tests passed") + else: + print("Graph tests failed") + +if __name__ == '__main__': + main() diff --git a/Tests/Helpers.py b/Tests/Helpers.py index b97ab73ae..156386e57 100644 --- a/Tests/Helpers.py +++ b/Tests/Helpers.py @@ -3,28 +3,33 @@ import shutil import stat import pprint -from time import sleep +import time from distutils.dir_util import copy_tree import shutil -import InternalConfig as iConfig +import TestConfig as testConfig import MachineConfigs as machine_configs +from pathlib import Path +from urllib.parse import urlparse + +def getExeDirectory(configuration): + exeDir = os.path.join(machine_configs.default_main_dir, 'Bin') -def get_executable_directory(configuration, test_set, runAsCollection): - if runAsCollection: - exe_dir = os.path.join(test_set, 'Bin') - else: - exe_dir = 'Bin' - if os.name == 'nt': - exe_dir = os.path.join(exe_dir, 'x64') + exeDir = os.path.join(exeDir, 'x64') if configuration.lower() == 'released3d12' or configuration.lower() == 'releasevk' : config = 'Release' else: config = 'Debug' - return os.path.join(exe_dir, config) + return os.path.join(exeDir, config) else: - return exe_dir - + return exeDir + +def findExecutable(config, exe): + exePath = os.path.join(getExeDirectory(config), exe) + if not os.path.isfile(exePath): + raise FileNotFoundError("Can't find the exe file `" + exe + "`") + return exePath + # Error if we failed to clean or make the correct directory. class CloneRepoCleanOrMakeError(Exception): pass @@ -33,71 +38,37 @@ class CloneRepoCleanOrMakeError(Exception): class CloneRepoCloneError(Exception): pass -def directory_make(destination): - if not os.path.isdir(destination): - try: - os.makedirs(destination) - return 0 - - except OSError: - print("Error trying to Create Directory : " + destination) - return None +def mkdir(dir): + dir = Path(dir) + if not dir.is_dir(): + os.makedirs(str(dir)) -def directory_clean(destination): - try: - remove_directory_return_code = 0 - if os.name == 'nt': - # Create the arguments. - batch_args = ["RemoveDirectoryTree.bat ", destination] - # Clean the Directory. - remove_directory_return_code = subprocess.call(batch_args) - - # Check if it was success. - if remove_directory_return_code != 0: - print("Error trying to clean Directory : " + destination + str(remove_directory_return_code)) - else: - # Clean the Directory. - shutil.rmtree(destination) - if not os.path.exists(destination): - os.makedirs(destination) - return remove_directory_return_code - - # Exception Handling. - except subprocess.CalledProcessError: - print("Error trying to clean Directory : " + destination) - # Return failure. - return None +def rmdir(dir): + dir = Path(dir) + if dir.is_dir(): + shutil.rmtree(dir, ignore_errors=True) - # Clean the directory if it exists, or make it if it does not. -def directory_clean_or_make(destination): - # Check if the Directory exists, and make it if it does not. - if not os.path.isdir(destination): - try: - os.makedirs(destination) - return 0 - except OSError: - print("Error trying to Create Directory : " + destination) - return None - else: - directory_clean(destination) +def cleanDir(dir): + rmdir(dir) + mkdir(dir) # Clone the Repository with the specified Arguments. def clone(repository, branch, destination): # Create the Destination Directory. - if directory_clean_or_make(destination) != 0 : + if cleanDir(destination) != 0 : raise CloneRepoCleanOrMakeError("Failed To Clean or Make Directory") # Clone the Specified Repository and Branch. try: - clone_return_code = subprocess.call(['git', 'clone', repository, destination, '-b', branch]) + errCode = subprocess.call(['git', 'clone', repository, destination, '-b', branch]) # Raise an exception if the subprocess did not run correctly. - if clone_return_code != 0 : + if errCode != 0 : raise CloneRepoCloneError('Error Cloning Repository : ' + repository + ' Branch : ' + branch + ' Destination : ' + destination + ' ') - return clone_return_code + return errCode # Exception Handling. except subprocess.CalledProcessError: @@ -105,85 +76,102 @@ def clone(repository, branch, destination): raise CloneRepoCloneError('Error Cloning Repository : ' + repository + ' Branch : ' + branch + ' Destination : ' + destination + ' ') -def open_file_dir(references_dir): - if os.path.isdir(references_dir): +def openFolderInExplorer(folder): + if os.path.isdir(folder): if os.name == 'nt': - subprocess.call('explorer.exe ' + references_dir ) + subprocess.call('explorer.exe ' + folder ) else: - subprocess.call('nautilus --browser ' + references_dir ) + subprocess.call('nautilus --browser ' + folder ) class GitError(Exception): pass # get branch name without having to store it in a config or require the user to install pygit -def get_git_branch_name(base_dir): +def getGitBranchName(baseDir): try: - git_file = open(os.path.join(base_dir, '.git/HEAD' )) - git_file_string = git_file.readline() + gitFile = open(os.path.join(baseDir, '.git/HEAD' )) + gitFileStr = gitFile.readline() except (IOError, OSError) as e: raise GitError(e.args) - - print(git_file_string) - - if git_file_string.find('ref: ') > -1: - git_file_string = git_file_string[5 : len(git_file_string)] - return git_file_string[git_file_string.rfind('/') + 1 : len(git_file_string) - 1] + + if gitFileStr.find('ref: ') > -1: + gitFileStr = gitFileStr[5 : len(gitFileStr)] + return gitFileStr[gitFileStr.rfind('/') + 1 : len(gitFileStr) - 1] return machine_configs.default_reference_branch_name # get branch name without having to store it in a config or require the user to install pygit -def get_git_url(base_dir): +def getGitUrl(baseDir): try: - git_file = open(os.path.join(base_dir, '.git/config' )) - git_file_string = git_file.read() + gitFile = open(os.path.join(baseDir, '.git/config' )) + gitFileStr = gitFile.read() except (IOError, OSError) as e: raise GitError(e.args) - ref = git_file_string.find('url = ') + ref = gitFileStr.find('url = ') if ref > -1: - rest = git_file_string[ref:len(git_file_string)] - git_url = rest[6 : rest.find('\n')] - return git_url + rest = gitFileStr[ref:len(gitFileStr)] + gitUrl = rest[6 : rest.find('\n')] + return gitUrl return machine_configs.default_reference_url +def getVcsRoot(baseDir): + url = getGitUrl(baseDir) + url = urlparse(url) + url = url.netloc.split('.') + for u in url: + if u.startswith("git@"): u = u.replace("git@", "") + if u == "gitlab-master" or u == "github": return u + print("Error. Unknown VCS root `" + url[0] + "`") + return url[0].lower() + + # Error if we failed to build the solution. class BuildSolutionError(Exception): pass -def build_solution(cloned_dir, relative_solution_filepath, configuration, rebuild): +def buildSolution(slnDir, slnFile, config, rebuild): if os.name == 'nt': - windows_build_script = "BuildSolution.bat" + winBuildScript = "BuildSolution.bat" try: # Build the Batch Args. buildType = "build" if rebuild: buildType = "rebuild" - batch_args = [windows_build_script, buildType, relative_solution_filepath, configuration.lower()] + slnPath = Path(slnDir) / Path(slnFile + ".sln") + batchArgs = [winBuildScript, buildType, str(slnPath), config.lower()] # Build Solution. - if subprocess.call(batch_args) == 0: + if subprocess.call(batchArgs) == 0: return 0 + else: + raise Exception() - except subprocess.CalledProcessError as subprocess_error: - raise BuildSolutionError("Error building solution : " + relative_solution_filepath + " with configuration : " + configuration.lower()) + except Exception: + raise BuildSolutionError("Error building solution : " + str(slnPath) + " with configuration : " + config.lower()) else: prevDir = os.getcwd() #Call Makefile os.chdir(cloned_dir) subprocess.call(['make', 'PreBuild', '-j8', '-k']) - subprocess.call(['make', 'All', '-j24', '-k']) + subprocess.call(['make', 'All', '-j24', '-k','TESTS=\'-D _TEST_\'']) os.chdir(prevDir) def isSupportedImageExt(file): - for ext in iConfig.ImageExtensions: + for ext in testConfig.imageExtensions: if file.endswith(ext): return True return False -def dispatch_email(subject, attachments): +def deletePackmanRepo(): + if os.path.isdir(machine_configs.packman_repo): + print("Deleting the packman repository") + rmdir(machine_configs.packman_repo) + +def dispatchEmail(subject, attachments): dispatcher = 'NvrGfxTest@nvidia.com' recipients = str(open(machine_configs.machine_email_recipients, 'r').read()) @@ -200,13 +188,13 @@ def dispatch_email(subject, attachments): command.append(attachment) subprocess.call(command) -def directory_copy(fromDirectory, toDirectory): +def copyDir(fromDirectory, toDirectory): print('Copying directory ' + fromDirectory + ' to ' + toDirectory) try: for subdir, dirs, files in os.walk(fromDirectory): - relative_file_path = subdir[len(fromDirectory) + 1 : len(subdir)] - to_path = os.path.join(toDirectory, relative_file_path) + relPath = subdir[len(fromDirectory) + 1 : len(subdir)] + to_path = os.path.join(toDirectory, relPath) if not os.path.isdir(to_path): os.mkdir(to_path) for file in files: @@ -217,7 +205,14 @@ def directory_copy(fromDirectory, toDirectory): print('Failed to copy reference files to server. Please check local directory.') return -def build_html_filename(tests_sets, configuration): +def createShortcut(source_path, link_file_path): + if os.name == 'nt': + # creates a junction to avoid requiring admin rights because of windows + subprocess.call(['mklink', link_file_path, source_path], shell=True) + else: + os.symlink(source_path, link_file_path) + +def buildHtmlFilename(tests_sets, configuration): header = "[SUCCESS]" for tests_set_key in tests_sets.keys(): if tests_sets[tests_set_key]['Success'] is False: @@ -226,3 +221,55 @@ def build_html_filename(tests_sets, configuration): return header + configuration + "_Results.html" +class ProcessFailed(RuntimeError): + pass + +class ProcessTimedoutError(Exception): + pass + +def runProcessAsync(cmdArgs): + try: + process = subprocess.Popen(cmdArgs, stderr = subprocess.PIPE, stdout = subprocess.PIPE) + startTime = time.time() + + # Wait for the process to finish. + while process.returncode is None: + process.poll() + + now = time.time() + diffTime = now - startTime + # If the process has taken too long, kill it. + if diffTime > machine_configs.machine_process_default_kill_time: + process.kill() + raise ProcessTimedoutError("Process ran for too long, had to kill it. Please verify that the program finishes within its hang time, and that it does not crash") + break + + e = "Process log:\n" + for string in process.stderr: + e += str(string.decode()) + + if process.returncode == 0: + return "Process " + cmdArgs[0] + " finished. " + e + + else: + e = "Process " + cmdArgs[0] + " failed with error " + str(process.returncode) + ". " + e + raise ProcessFailed(e) + + + except(NameError, IOError, OSError) as e: + print(e.args) + raise RuntimeError('Error when trying to run "' + cmdArgs + '"') + +def buildRefSubFolder(args): + ref = os.path.join(args.vcs_root, os.path.join(args.machine_name, os.path.join(args.branch_name, args.build_config))) + return ref + +def mirror_folders(source, dst): + if not os.name == 'nt': + raise RuntimeError("mirror_folders() is not implemented for this OS") + robocopy = ["Robocopy.exe", source, dst, "/MIR", "/FFT", "/Z", "/XA:H", "/W:5", "/LOG:robocopy.txt", "/np"] + try: + subprocess.check_call(robocopy) + except(subprocess.CalledProcessError) as e: + if e.returncode > 7: + raise RuntimeError("Mirroring folders failed") diff --git a/Tests/InternalConfig.py b/Tests/InternalConfig.py deleted file mode 100644 index 688b42f58..000000000 --- a/Tests/InternalConfig.py +++ /dev/null @@ -1,64 +0,0 @@ -import os - -TestConfig = {} -TestConfig['Scenes'] = [ "Arcade/Arcade.fscene", "SunTemple/SunTemple.fscene", "Bistro/Bistro_Interior.fscene" ] -TestConfig['Images'] = [ "StockImage.jpg" ] -TestConfig['Duration'] = 360 -TestConfig['Frames'] = [ 16, 32, 128, 256] -TestConfig['DefaultConfiguration'] = 'ReleaseD3D12' -TestConfig['LocalTestingDir'] = 'testing' -TestConfig['Tolerance'] = 200.0 -TestConfig['Tolerance_Lower'] = 0.1 -TestConfig['FixedTimeDelta'] = 0.01666 - -# Relative to root directory -IgnoreDirectories = {} -IgnoreDirectories['ReleaseD3D12'] = [] -IgnoreDirectories['ReleaseVK'] = [ os.path.join('Framework', 'Internals') ] - -# Supported image extensions -ImageExtensions = ['.png', '.jpg', '.tga', '.bmp', '.pfm', '.exr'] - -# get 'static' part of the arguments -def get_config_arguments(): - current_args = '-testFrames ' - - for testFrame in TestConfig['Frames']: - current_args = current_args + str(testFrame) + ' ' - - if 'Duration' in TestConfig: - current_args = current_args + '-shutdownframe ' + str(TestConfig['Duration']) + ' '; - - if 'FixedTimeDelta' in TestConfig: - current_args = current_args + '-fixedtimedelta ' + str(TestConfig['FixedTimeDelta']) + ' '; - - return current_args - - -test_arguments = get_config_arguments() -if os.name == 'nt': - viewer_executable = 'RenderGraphViewer.exe' -else: - viewer_executable = 'RenderGraphViewer' - -if os.name == 'nt': - unit_tests_executable = 'FalcorTest.exe' -else: - unit_tests_executable = 'FalcorTest' -num_scenes = len(TestConfig["Scenes"]) -num_images = len(TestConfig["Images"]) - -# get the arguments that change for each test -def get_next_scene_args(scene_index): - if scene_index >= num_scenes : - scene_index = num_scenes - 1 - - args = '-scene ' + TestConfig["Scenes"][scene_index] + ' ' - return args - -def get_next_image_args(image_index): - if image_index >= num_images : - image_index = num_images - 1 - - args = '-image ' + TestConfig['Images'][image_index] + ' ' - return args \ No newline at end of file diff --git a/Tests/MachineConfigs.py b/Tests/MachineConfigs.py index 36f962329..640fe4dec 100644 --- a/Tests/MachineConfigs.py +++ b/Tests/MachineConfigs.py @@ -5,26 +5,28 @@ 'Windows': { 'Destination Target' : 'C:\\Falcor\\GitHub\\', - 'Reference Target' : '\\\\netapp-wa02\\public\\Falcor\\References\\', + 'Remote References' : '\\\\netapp-wa02\\public\\Falcor\\References\\', 'Email List' : '\\\\netapp-wa02\\public\\Falcor\\email.txt', 'Results Summary Target' : '\\\\netapp-wa02\\public\\Falcor\\GitHub\\Results\\', 'Default Main Directory' : '..\\', - 'Results Cache Directory' : '\\\\netapp-wa02\\public\\Falcor\\ResultsCache\\' - + 'Results Cache Directory' : 'C:\\FalcorResults\\', + 'Packman Repo Path' : 'C:\\packman-repo\\', + 'Local References' : 'C:\\FalcorRefs\\' }, 'Linux': { 'Destination Target' : '/home/nvrgfxtest/Desktop/FalcorGitHub/', - 'Reference Target' : '/media/netapp/Falcor/References/', + 'Remote References' : '/media/netapp/Falcor/References/', 'Email List' : '/media/netapp/Falcor/email.txt', 'Results Summary Target' : '/media/netapp/Falcor/GitHub/Results/', 'Default Main Directory' : '../', - 'Results Cache Directory' : '/media/netapp/Falcor/ResultsCache/' + 'Results Cache Directory' : '/home/FalcorResults/', + 'Packman Repo Path' : '/packman-repo/', + 'Local References' : '/home/nvrgfxtest/FalcorRefs' } } machine_process_default_kill_time = 1200.0 -machine_relative_checkin_local_results_directory = os.path.join('TestsResults', 'local-results-directory') if os.name == 'nt': machine_name = os.environ['COMPUTERNAME'] @@ -34,14 +36,16 @@ machine_name = socket.gethostname() platform_data = json_data['Linux'] +packman_repo = platform_data['Packman Repo Path']; results_cache_directory = platform_data['Results Cache Directory']; default_reference_url = 'https://github.com/NVIDIAGameWorks/Falcor' machine_name = machine_name.lower() -machine_reference_directory = platform_data['Reference Target'] +remote_reference_directory = platform_data['Remote References'] +local_reference_directory = platform_data['Local References'] destination_target = platform_data['Destination Target'] machine_email_recipients = platform_data['Email List'] machine_results_summary_target = platform_data['Results Summary Target'] #for running test sets, not collections. Like check in tests default_reference_machine_name = 'default' -default_reference_branch_name = 'master' +default_reference_branch_name = 'develop' default_main_dir = platform_data['Default Main Directory'] \ No newline at end of file diff --git a/Tests/CloneRepo.py b/Tests/Old/CloneRepo.py similarity index 100% rename from Tests/CloneRepo.py rename to Tests/Old/CloneRepo.py diff --git a/Tests/CollectAndEmailResults.py b/Tests/Old/CollectAndEmailResults.py similarity index 100% rename from Tests/CollectAndEmailResults.py rename to Tests/Old/CollectAndEmailResults.py diff --git a/Tests/GenerateReferences.py b/Tests/Old/GenerateReferences.py similarity index 86% rename from Tests/GenerateReferences.py rename to Tests/Old/GenerateReferences.py index 4d5a2b5bf..ab9753567 100644 --- a/Tests/GenerateReferences.py +++ b/Tests/Old/GenerateReferences.py @@ -50,7 +50,7 @@ def generate_references_remote(branch_name, git_path, tests_directory): buildTypeIds = get_generate_references_build_type_ids() for buildTypeId in buildTypeIds: - remoteTests.start_build(username, password, xml_file_path, branch_name, git_path, tests_directory, buildTypeId) + remoteTests.start_build(username, xml_file_path, branch_name, git_path, tests_directory, buildTypeId) sleep(4) getBuildStatus.wait_for_running_builds(buildTypeIds) @@ -69,6 +69,9 @@ def main(): # Add argument for specifying to only build local instead of dispatching to teamcity parser.add_argument('-lo', '--local_only', action='store_true', help='Generate local references instead of teamcity machines generating references') + # Add argument for specifying to only build local instead of dispatching to teamcity + parser.add_argument('-nr', '--no_rebuild', action='store_true', help='Build the solution instead of rebuilding it') + # Add argument for specifying dispatching to teamcity instead of only building locally parser.add_argument('-rb', '--remote', action='store_true', help='Generate references on all teamcity test machines') @@ -77,23 +80,17 @@ def main(): # Add argument for only using a specified graph file in the directory parser.add_argument('-gf', '--graph_file', action='store', help='Specify graph file to use within the tests directory') - - # Add argument for only using a specified graph within the directory - parser.add_argument('-gn', '--graph_name', action='store', help='Specify graph name to use within the tests directory') - + # Add argument for branch name. If this is not specified the script will look in the .git directory parser.add_argument('-bn', '--branch_name', action='store', help='Name of the current checkout branch') - # Subfolder wintin the references directory. Set with the build machine name by the test servers - parser.add_argument('-rsf', '--reference_sub_folder', action='store', help='Optional sub folder name within references directory'); + # Subfolder within the references directory. Set with the build machine name by the test servers + parser.add_argument('-mn', '--machine_name', action='store', help='Optional sub folder name within references directory'); # Add argument for specifying to only generate references that are missing from the directory parser.add_argument('-m', '--generate_missing', action='store_true', help='Only generate missing reference images.'); - # Add argument for rebuilding project. The build agents will always do this. - parser.add_argument('-r', '--rebuild', action='store_true', help='Clean and rebuild the project.'); - - # Add argument for specifying specific git url. shouldn't do this + # Add argument for specifying specific git url. shouldn't do this unless parser.add_argument('-url', '--git_url', action='store', help='Url for the repository. Do not use this uless you have to.'); # Parse the Arguments. @@ -128,8 +125,8 @@ def main(): if args.upload and args.generate_missing: print('Copying available references from remote netapp server') - if args.reference_sub_folder: - target_dir = os.path.join(machine_configs.machine_reference_directory, args.reference_sub_folder) + if args.machine_name: + target_dir = os.path.join(machine_configs.machine_reference_directory, args.machine_name) target_dir = os.path.join(target_dir, branch_name) else: target_dir = os.path.join(machine_configs.machine_reference_directory, branch_name) @@ -143,6 +140,10 @@ def main(): errors = {} + path_to_bin = os.path.join( root_dir, 'Bin') + if os.path.isdir(path_to_bin): + helpers.directory_clean(path_to_bin) + if not args.local_only: if args.git_url: git_path = args.git_url @@ -152,13 +153,16 @@ def main(): generate_references_remote(branch_name, git_path, args.tests_directory) else: executable_filepath = helpers.get_executable_directory(target_configuration, '', False); - executable_filepath = os.path.join(os.path.join(root_dir, executable_filepath), iConfig.viewer_executable) - + executable_filepath = os.path.join(os.path.join(root_dir, executable_filepath), iConfig.mogwai_executable) + + if not arg.no_rebuild: + helpers.deletePackmanRepo() + # Build the falcor solution. Run build target on render pass project. - helpers.build_solution(root_dir, os.path.join(root_dir, 'Falcor.sln'), target_configuration, args.rebuild) + helpers.build_solution(root_dir, os.path.join(root_dir, 'Falcor.sln'), target_configuration, not args.no_rebuild) test_counter = 0; if args.tests_directory: - errors = rPT.run_graph_pass_test(executable_filepath, args.tests_directory, args.graph_file, args.graph_name, references_dir, args.generate_missing) + errors = rPT.run_graph_pass_test(executable_filepath, args.tests_directory, args.graph_file, references_dir, args.generate_missing) else: for subdir, dirs, files in os.walk(root_dir): ignoreThisDir = False @@ -172,18 +176,15 @@ def main(): continue if subdir.lower().endswith(iConfig.TestConfig['LocalTestingDir']): - new_errors = rPT.run_graph_pass_test(executable_filepath, subdir, args.graph_file, args.graph_name, references_dir, args.generate_missing) - test_counter = test_counter + 1 - if test_counter == 2: - break; + new_errors = rPT.run_graph_pass_test(executable_filepath, subdir, args.graph_file, references_dir, args.generate_missing) for error_key in new_errors.keys(): errors[error_key] = new_errors[error_key] # copy top level reference directory to netapp if (args.upload): print('Uploading references to remote netapp server') - if args.reference_sub_folder: - target_dir = os.path.join(machine_configs.machine_reference_directory, args.reference_sub_folder) + if args.machine_name: + target_dir = os.path.join(machine_configs.machine_reference_directory, args.machine_name) target_dir = os.path.join(target_dir, branch_name) else: target_dir = os.path.join(machine_configs.machine_reference_directory, branch_name) diff --git a/Tests/GetBuildStatus.py b/Tests/Old/GetBuildStatus.py similarity index 90% rename from Tests/GetBuildStatus.py rename to Tests/Old/GetBuildStatus.py index f94db5d48..b0f259677 100644 --- a/Tests/GetBuildStatus.py +++ b/Tests/Old/GetBuildStatus.py @@ -3,6 +3,7 @@ import getpass import argparse import xml.etree.ElementTree as ET +import TeamCityCommon from TeamCityCommon import connect from TeamCityCommon import server_url from TeamCityCommon import project_url @@ -18,12 +19,10 @@ class ConnectError(Exception): pass def get_build_types(): - r = urllib.request.Request(server_url + 'app/rest/projects/id:Falcor/buildTypes') - return urllib.request.urlopen(r) + return TeamCityCommon.get_request('app/rest/projects/id:Falcor/buildTypes') def get_builds(suffix): - r = urllib.request.Request(project_url + suffix)# urllib.parse.urlencode(get_fields).encode()) - return urllib.request.urlopen(r) + return TeamCityCommon.get_request(project_url + suffix) def get_all_builds(): return get_builds(',running:any') @@ -35,8 +34,7 @@ def get_queued_builds(): return get_builds(',state:queued') def get_vcs_instances(): - r = urllib.request.Request(server_url + 'app/rest/vcs-root-instances/?locator=project:Falcor') - return urllib.request.urlopen(r) + return TeamCityCommon.get_request('app/rest/vcs-root-instances/?locator=project:Falcor') def get_running_buildIds(): buildIds = [] diff --git a/Tests/RemoveDirectoryTree.bat b/Tests/Old/RemoveDirectoryTree.bat similarity index 100% rename from Tests/RemoveDirectoryTree.bat rename to Tests/Old/RemoveDirectoryTree.bat diff --git a/Tests/Old/RunAllTests.py b/Tests/Old/RunAllTests.py new file mode 100644 index 000000000..5e91073e8 --- /dev/null +++ b/Tests/Old/RunAllTests.py @@ -0,0 +1,208 @@ +import subprocess +import argparse +import os +from datetime import date +import shutil +import stat +import sys +import json +import pprint +import getpass +from time import sleep + +import xml.etree.ElementTree as ET +import MachineConfigs as machine_configs +import TestConfig as config +import RunPassTests as rPT +import Helpers as helpers +import sys +import WriteTestResultsToHTML as writeTestResultsToHTML +from TeamCityCommon import connect +import StartBuildTest as remoteTests +import GetBuildStatus as getBuildStatus + +xml_file_path = './build.xml' + +class PassTestsError(Exception): + pass + +def get_generate_references_build_type_ids(): + build_types = getBuildStatus.get_build_types(); + dataString = str(build_types.read().decode()) + xmldata = ET.fromstring(dataString) + build_type_ids = [] + + for node in xmldata.iter(): + for buildType in node.findall('buildType'): + buildTypeId = str(buildType.get('id')) + if buildTypeId.find('Generate') != -1: + build_type_ids.append(buildTypeId) + + return build_type_ids + +def generate_references_remote(branch_name, git_path, tests_directory): + username = input('Enter username for teamcity.nvidia.com: ') + getPassPrompt = 'Enter teamcity password for user ' + username + ':' + password = getpass.getpass(prompt=getPassPrompt ) + + connect(username, password) + + buildTypeIds = get_generate_references_build_type_ids() + + for buildTypeId in buildTypeIds: + remoteTests.start_build(username, xml_file_path, branch_name, git_path, tests_directory, buildTypeId) + + sleep(4) + getBuildStatus.wait_for_running_builds(buildTypeIds) + +def main(): + + # Argument Parser. + parser = argparse.ArgumentParser() + + # Add argument for specifing build configuration for the test + parser.add_argument('--build_configuration', action='store', help='Build configuration for test. ReleaseD3D12 by default') + + # Add argument for specifying to only build local instead of dispatching to teamcity + parser.add_argument('--local', action='store_true', help='Generate local references') + + # Add argument for specifying to only build local instead of dispatching to teamcity + parser.add_argument('--rebuild', action='store_true', help='Force solution rebuild') + + # Add argument for specifying dispatching to teamcity instead of only building locally + parser.add_argument('--remote', action='store_true', help='Generate references on all teamcity test machines') + + # Add argument to specify to upload resources to the data server + parser.add_argument('--upload', action='store_true', help='Upload the references to netapp, if tests are local') + + # Add argument for branch name. If this is not specified the script will look in the .git directory + parser.add_argument('--branch_name', action='store', help='Name of the current checkout branch') + + # Subfolder within the references directory. Set with the build machine name by the test servers + parser.add_argument('--machine_name', action='store', help='Optional sub folder name within references directory'); + + # Add argument for specifying to only generate references that are missing from the directory + parser.add_argument('--generate_missing', action='store_true', help='Only generate missing reference images.'); + + # Parse the Arguments. + args = parser.parse_args() + + if not args.remote and not args.local: + raise(PassTestsError("Please specify 'local' or 'remote' within the script arguments.")) + + if args.build_configuration: + target_configuration = args.build_configuration + else: + target_configuration = config.defaultConfig + + # This assumes the user always runs the script in the /Tests directory + if args.branch_name: + branch_name = args.branch_name + else: + branch_name = helpers.get_git_branch_name(root_dir); + + references_dir = os.path.join('TestsResults', branch_name) + if not args.generate_missing: + helpers.directory_clean_or_make(references_dir) + else: + helpers.directory_make(references_dir) + + references_dir = os.path.join(references_dir, target_configuration); + if not args.generate_missing: + helpers.directory_clean_or_make(references_dir) + else: + helpers.directory_make(references_dir) + + if args.upload and args.generate_missing: + print('Copying available references from remote netapp server') + if args.machine_name: + target_dir = os.path.join(machine_configs.machine_reference_directory, args.machine_name) + target_dir = os.path.join(target_dir, branch_name) + else: + target_dir = os.path.join(machine_configs.machine_reference_directory, branch_name) + + target_dir = os.path.join(target_dir, target_configuration) + if os.path.isdir(target_dir): + helpers.directory_copy(target_dir, references_dir) + + if not args.tests_directory: + print('No path specified. Will generate reference images for all passes.') + + errors = {} + + path_to_bin = os.path.join( root_dir, 'Bin') + if os.path.isdir(path_to_bin): + helpers.directory_clean(path_to_bin) + + if not args.local_only: + if args.git_url: + git_path = args.git_url + else: + git_path = helpers.get_git_url(root_dir) + + generate_references_remote(branch_name, git_path, args.tests_directory) + else: + executable_filepath = helpers.get_executable_directory(target_configuration, '', False); + executable_filepath = os.path.join(os.path.join(root_dir, executable_filepath), iConfig.mogwai_executable) + + if not arg.no_rebuild: + helpers.deletePackmanRepo() + + # Build the falcor solution. Run build target on render pass project. + helpers.build_solution(root_dir, os.path.join(root_dir, 'Falcor.sln'), target_configuration, not args.no_rebuild) + test_counter = 0; + if args.tests_directory: + errors = rPT.run_graph_pass_test(executable_filepath, args.tests_directory, args.graph_file, references_dir, args.generate_missing) + else: + for subdir, dirs, files in os.walk(root_dir): + ignoreThisDir = False + for ignoreDir in iConfig.IgnoreDirectories[target_configuration]: + ignore_abs_path = os.path.abspath(os.path.join(root_dir, str(ignoreDir))) + current_abs_path = os.path.abspath(subdir) + if (os.path.commonpath([ignore_abs_path]) == os.path.commonpath([ignore_abs_path, current_abs_path])): + ignoreThisDir = True + break; + if ignoreThisDir: + continue + + if subdir.lower().endswith(iConfig.TestConfig['LocalTestingDir']): + new_errors = rPT.run_graph_pass_test(executable_filepath, subdir, args.graph_file, references_dir, args.generate_missing) + for error_key in new_errors.keys(): + errors[error_key] = new_errors[error_key] + + # copy top level reference directory to netapp + if (args.upload): + print('Uploading references to remote netapp server') + if args.machine_name: + target_dir = os.path.join(machine_configs.machine_reference_directory, args.machine_name) + target_dir = os.path.join(target_dir, branch_name) + else: + target_dir = os.path.join(machine_configs.machine_reference_directory, branch_name) + + all_results_data = [] + + target_dir = os.path.join(target_dir, target_configuration) + helpers.directory_make(target_dir) + helpers.directory_copy(references_dir, target_dir) + + for subdir, dirs, files in os.walk(target_dir): + for file in files: + all_results_data.append(os.path.abspath(os.path.join(subdir, file))) + + # output html file with references + html_file_content = writeTestResultsToHTML.write_generate_references_to_html(target_dir, all_results_data, errors) + helpers.directory_make(machine_configs.machine_relative_checkin_local_results_directory) + html_file_path = os.path.join(target_dir, "GenerateReferences_Results.html") + html_file = open(html_file_path, 'w') + html_file.write(html_file_content) + html_file.close() + + print ('Please confirm that your output images are correct') + + #open file browser to generate references + target_dir = os.path.join(machine_configs.machine_reference_directory, branch_name) + print (target_dir) + helpers.open_file_dir(target_dir) + +if __name__ == '__main__': + main() diff --git a/Tests/RunPassTests.py b/Tests/Old/RunPassTests.py similarity index 63% rename from Tests/RunPassTests.py rename to Tests/Old/RunPassTests.py index 7b8c915c3..b1c7380cd 100644 --- a/Tests/RunPassTests.py +++ b/Tests/Old/RunPassTests.py @@ -10,6 +10,7 @@ import json import pprint import webbrowser +import random import WriteTestResultsToHTML as writeTestResultsToHTML import CompareOutput as compareOutput @@ -54,130 +55,15 @@ def run_test_run(executable_filepath, current_arguments, output_file_base_name, raise TestsSetError('Error when trying to run ' + executable_filepath + ' ' + current_arguments + ' ' + 'with outputfilename ' + output_file_base_name + ' and outputdir ' + output_directory) -def run_pass_test(executable_filepath, file_path, graph_name, output_directory, generate_missing): - print('Running tests for graphs in: ' + file_path) +def run_pass_test(executable_filepath, file_path, output_directory, generate_missing): + print('Running tests for graph file ' + file_path) print('Output directory set to' + output_directory) - graph_names = [] - num_image_loader_passes = [] # number of image nodes per graph in file - scenes_loaded = [] # if there is a graph loaded - images_loaded = [] # if there is a graph loaded - no_default_scenes = [] # if disableLoadDefaultScene is called in graph errors = [] - - #parse render graph file for graphs. - graph_file = open( file_path ).read() - file_ast = ast.parse(graph_file) - graph_func_prefix = 'render_graph_' - - # grab the name of each create function from syntax tree - for func in file_ast.body: - num_img_loaders = 0 - graph_loads_scene = False - graph_loads_image = False - no_default_scene = False - - if(isinstance(func, ast.FunctionDef)): - # check all statements in the tree to find calls to create an image loader node. - for expr in ast.walk(func): - if isinstance(expr, ast.Call): - # print(ast.dump(expr)) # this is useful for debugging this - if isinstance(expr.func, ast.Name): - if expr.func.id == "createRenderPass": - if expr.args[0].s == "ImageLoader": - num_img_loaders = num_img_loaders + 1 - if len(expr.args) >= 1: - passDictionary = expr.args[1] - if isinstance(passDictionary, ast.Dict): - for key in passDictionary.keys: - if(key.s == 'fileName'): - # note: the ast's 'dictionary' is not the same as a dictionary - graph_loads_image = len(passDictionary.values[0].s) > 0 - else: - # member functions so its a bit differently - if isinstance(expr.func, ast.Attribute): - if expr.func.attr == "setScene": - graph_loads_scene = True - if expr.func.attr == 'disableLoadDefaultScene': - no_default_scene = True - - num_image_loader_passes.append(num_img_loaders) - scenes_loaded.append(graph_loads_scene) - images_loaded.append(graph_loads_image) - no_default_scenes.append(no_default_scene) - # function must fit definition of 'render_graph_' + graph_name - if(func.name.startswith(graph_func_prefix)): - graph_names.append(func.name[len(graph_func_prefix) : len(func.name)]) - # run a test on each graph in the file. index = 0 - for graphName in graph_names: - if graph_name and graph_name != graphName: - continue - - print('Running tests with \'' + graphName + '\' in ' + file_path) - start_viewer_args = iConfig.test_arguments + ' -graphFile ' + file_path + ' -graphname ' + graphName + ' ' - - run_image_tests = num_image_loader_passes[index] > 0 - run_scene_tests = not (no_default_scenes[index] and (not scenes_loaded[index])) - run_all_scenes = (not scenes_loaded[index]) and run_scene_tests - run_all_images = not images_loaded[index] - - if run_all_scenes: - num_scenes = iConfig.num_scenes - else: - num_scenes = 1 - - if run_all_images: - num_images = iConfig.num_images - else: - num_images = 1 - - for test_index in range(0, max(num_scenes, num_images) ): - viewer_args = start_viewer_args - output_file_base_name = graphName + '_' - - if run_scene_tests: - scene_viewer_args = iConfig.get_next_scene_args(test_index) - viewer_args = scene_viewer_args + viewer_args - input_arg = scene_viewer_args.split()[1] - output_file_base_name = output_file_base_name + os.path.splitext(os.path.basename(input_arg))[0] + '_' - - if run_image_tests: - image_viewer_args = iConfig.get_next_image_args(test_index) - input_arg = image_viewer_args.split()[1] - output_image_file_base_name = output_file_base_name + os.path.splitext(os.path.basename(input_arg))[0] + '_' - dont_run = False - - viewer_args_with_images = viewer_args + image_viewer_args - if generate_missing: - for subdir, dirs, files in os.walk(output_directory): - for file in files: - if (file.startswith(output_image_file_base_name)): - print('Skipping test : ' + output_image_file_base_name + ' . Output files already exists') - dont_run = True - break; - if dont_run: - continue; - output_results = run_test_run(executable_filepath, viewer_args_with_images, output_image_file_base_name, output_directory) - errors.append(output_results); - else: - dont_run = False - if generate_missing: - for subdir, dirs, files in os.walk(output_directory): - for file in files: - if (file.startswith(output_file_base_name)): - print('Skipping test : ' + output_file_base_name + ' . Output files already exists') - dont_run = True - break; - if dont_run: - continue; - output_results = run_test_run(executable_filepath, viewer_args, output_file_base_name, output_directory) - errors.append(output_results); - - print('\n') - index = index + 1 + return errors @@ -222,7 +108,8 @@ def run_unit_tests(executable_filepath, unit_test_outputfile_path, regex_filter) print(e.args) raise TestsSetError('Error when trying to run unit tests') -def run_graph_pass_test(executable_filepath, path_to_tests, graph_file_name, graph_name, output_directory, generate_missing): + +def run_graph_pass_test(executable_filepath, path_to_tests, graph_file_name, output_directory, generate_missing): renderGraphFiles = [] errors = {} @@ -250,7 +137,7 @@ def run_graph_pass_test(executable_filepath, path_to_tests, graph_file_name, gra else: helpers.directory_make(graph_output_directory) errors[graphFile] = [] - errors[graphFile] = run_pass_test(executable_filepath, graphFile, graph_name, graph_output_directory, generate_missing) + errors[graphFile] = run_pass_test(executable_filepath, graphFile, graph_output_directory, generate_missing) return errors @@ -267,15 +154,12 @@ def main(): # Add argument for only using a specified graph file in the directory parser.add_argument('-gf', '--graph_file', action='store', help='Specify graph file to use within the tests directory') - - # Add argument for only using a specified graph within the directory - parser.add_argument('-gn', '--graph_name', action='store', help='Specify graph name to use within the tests directory') - + #Add the argument for only doing comparisons with the last generated references parser.add_argument('-cmp', '--compare_only', action='store_true', help='Do not generate local images. Only compare last generated.'); # Subfolder wintin the references directory. Set with the build machine name by the test servers - parser.add_argument('-rsf', '--reference_sub_folder', action='store', help='Optional sub folder name within references directory'); + parser.add_argument('-mn', '--machine_name', action='store', help='Optional sub folder name within references directory'); # Add argument for branch name. If this is not specified the script will look in the .git directory parser.add_argument('-bn', '--branch_name', action='store', help='Name of the current checkout branch') @@ -284,17 +168,20 @@ def main(): parser.add_argument('-rb', '--reference_branch_name', action='store', help='Name of the branch in which the references were generated from') # Argument for repostiory name in front of reference machine name - parser.add_argument('-repo', '--repository_id', action='store', help='Id name for the checkout repository appended in front of the reference sub folder name') + parser.add_argument('-repo', '--repository_id', action='store', help='Id name for the checkout repository appended in front of the machine name') # Add argument to specify to upload results to the data server cache parser.add_argument('-u', '--upload', action='store_true', help='Upload the test results to netapp server') - # Add argument to specify regex filter for unit tests run along side the pass tests. - parser.add_argument('-regex', '--unit_test_filter', action='store', help='Specify regex filter for unit tests run along side the pass tests') - # Add argument to specify comparing only to local references - parser .add_argument('-l', '--local_only', action ='store_true') + parser.add_argument('-l', '--local_only', action ='store_true') + + # Add argument to specify comparing only to netapp references + parser.add_argument('-r', '--remote', action ='store_true') + # Add argument to specify regex filter for unit tests run along side the pass tests. + parser.add_argument('-regex', '--unit_test_filter', action='store', help='Specify regex filter for unit tests run along side the pass tests') + # Parse the Arguments. args = parser.parse_args() @@ -307,18 +194,28 @@ def main(): root_dir = machine_configs.default_main_dir executables_filepath = helpers.get_executable_directory(target_configuration, '', True); executables_filepath = os.path.join(root_dir, executables_filepath) - executable_filepath = os.path.join(executables_filepath, iConfig.viewer_executable) + executable_filepath = os.path.join(executables_filepath, iConfig.mogwai_executable) unit_tests_executable_filepath = os.path.join(executables_filepath, iConfig.unit_tests_executable) + if args.branch_name: branch_name = args.branch_name else: branch_name = helpers.get_git_branch_name(root_dir); + if not args.remote and not args.local_only: + raise TestsSetError("Please specifiy --local_only or --remote for references source.") + + if args.remote: + references_dir = machine_configs.machine_reference_directory + else: + if args.local_only: + references_dir = 'TestsResults' + results_dir = machine_configs.machine_relative_checkin_local_results_directory - if args.reference_sub_folder: + if args.machine_name: if args.repository_id: results_dir = os.path.join(results_dir, args.repository_id) - results_dir = os.path.join(results_dir, args.reference_sub_folder) + results_dir = os.path.join(results_dir, args.machine_name) results_dir = os.path.join(results_dir, branch_name) else: results_dir = os.path.join(results_dir, branch_name) @@ -328,8 +225,6 @@ def main(): if not args.compare_only: helpers.directory_clean_or_make(results_dir) - references_dir = machine_configs.machine_reference_directory - if args.repository_id: references_dir = os.path.join(references_dir, args.repository_id) @@ -338,16 +233,22 @@ def main(): print('No path specified. Will run full tests for all passes.') path_to_bin = os.path.join( root_dir, 'Bin') - if os.path.isdir(path_to_bin): - helpers.directory_clean(path_to_bin) + + if os.name == 'nt': + path_to_bin = os.path.join( path_to_bin, 'x64') errors = {} if not args.compare_only: + if os.path.isdir(path_to_bin): + helpers.directory_clean(path_to_bin) + + helpers.deletePackmanRepo() + # Build the falcor solution. Run build target on render pass project. - helpers.build_solution(root_dir, os.path.join(root_dir, 'Falcor.sln'), target_configuration, False) + helpers.build_solution(root_dir, os.path.join(root_dir, 'Falcor.sln'), target_configuration, True) if args.tests_directory: - errors = run_graph_pass_test(executable_filepath, args.tests_directory, args.graph_file, args.graph_name, results_dir, False) + errors = run_graph_pass_test(executable_filepath, args.tests_directory, args.graph_file, results_dir, False) else: for subdir, dirs, files in os.walk(root_dir): @@ -362,7 +263,7 @@ def main(): continue if subdir.lower().endswith('testing'): - new_errors = run_graph_pass_test(executable_filepath, subdir, args.graph_file, args.graph_name, results_dir, False) + new_errors = run_graph_pass_test(executable_filepath, subdir, args.graph_file, results_dir, False) for error_key in new_errors.keys(): errors[error_key] = new_errors[error_key] @@ -378,10 +279,17 @@ def main(): if args.upload: # upload the local images to remote cache folder target_dir = os.path.join(machine_configs.results_cache_directory, branch_name) - if args.reference_sub_folder: + if args.machine_name: if args.repository_id: target_dir = os.path.join(target_dir, args.repository_id) - target_dir = os.path.join(target_dir, args.reference_sub_folder) + target_dir = os.path.join(target_dir, args.machine_name) + + # add unique tag for this test + random_value = random.randint(100000, 999999) + while os.path.isdir(os.path.join(target_dir, str(random_value))): + random_value = random.randint(100000, 999999) + + target_dir = os.path.join(target_dir, str(random_value)) target_dir = os.path.join(target_dir, target_configuration) compare_results_dir = target_dir if not args.tests_directory: @@ -394,34 +302,46 @@ def main(): # compare tests to references for references_subdir, dirs, files in os.walk(references_dir): #for references_subdir in os.listdir(references_dir): subdir = os.path.join(references_dir, references_subdir) - if args.reference_sub_folder and (args.reference_sub_folder != os.path.basename(references_subdir)): + if args.machine_name and (args.machine_name != os.path.basename(references_subdir)): continue; if os.path.isdir(subdir): + source_subdir = os.path.join(os.path.join(subdir, branch_name), target_configuration) subdir = os.path.join( os.path.join(subdir, reference_branch_name), target_configuration) + b_run_compare = True if not os.path.isdir(subdir): - print('No references for ' + target_configuration + ' on ' + references_subdir) - else: - all_results_data[os.path.join(subdir, references_subdir)] = compareOutput.compare_all_images(compare_results_dir, subdir, compareOutput.default_compare) - if args.reference_sub_folder: + # check references in this branches references for new tests + if not os.path.isdir(source_subdir) or source_subdir == subdir: + b_run_compare = False; + print('No references for ' + target_configuration + ' on ' + references_subdir) + if b_run_compare: + all_results_data[os.path.join(subdir, references_subdir)] = compareOutput.compare_all_images(compare_results_dir, subdir, source_subdir, compareOutput.default_compare) + if args.machine_name: break; + if args.local_only: + subdir = references_dir + if args.machine_name: + subdir = os.path.join(subdir, args.machine_name) + source_subdir = os.path.join(os.path.join(subdir, branch_name), target_configuration) + subdir = os.path.join( os.path.join(references_dir, reference_branch_name), target_configuration) + all_results_data[os.path.join(subdir, references_dir)] = compareOutput.compare_all_images(compare_results_dir, subdir, source_subdir, compareOutput.default_compare) # write comparison to html file unit_test_filename = "UnitTestOutput_" + target_configuration + ".txt" html_file_content = writeTestResultsToHTML.write_test_set_results_to_html(all_results_data, errors) html_file_name = helpers.build_html_filename(all_results_data, target_configuration) artifacts_path = machine_configs.machine_relative_checkin_local_results_directory - if args.reference_sub_folder: + if args.machine_name: if args.repository_id: artifacts_path = os.path.join(artifacts_path, args.repository_id) - artifacts_path = os.path.join(artifacts_path, args.reference_sub_folder) + artifacts_path = os.path.join(artifacts_path, args.machine_name) artifacts_path = os.path.join(artifacts_path, branch_name) helpers.directory_make(machine_configs.machine_relative_checkin_local_results_directory) artifacts_path = os.path.join(artifacts_path, target_configuration) - html_file_path = os.path.join(artifacts_path, html_file_name) - print('Writing comparison file to ' + html_file_path) - html_file = open(html_file_path, 'w') + html_file_full_path = os.path.join(artifacts_path, html_file_name) + print('Writing comparison file to ' + html_file_full_path) + html_file = open(html_file_full_path, 'w') html_file.write(html_file_content) html_file.close() @@ -430,22 +350,28 @@ def main(): regex = str(args.unit_test_filter) unit_test_outputfile_path = os.path.join(artifacts_path, unit_test_filename) - run_unit_tests(unit_tests_executable_filepath, unit_test_outputfile_path, regex) - unit_test_out_file = open(unit_test_outputfile_path, 'r') - file_output_string = unit_test_out_file.read() + if not args.compare_only: + run_unit_tests(unit_tests_executable_filepath, unit_test_outputfile_path, regex) + + unit_test_out_file = open(unit_test_outputfile_path, 'r') + file_output_string = unit_test_out_file.read() - if not len(file_output_string): - print ('[Error] no output from unit tests.') - else: - print (file_output_string) + if not len(file_output_string): + print ('[Error] no output from unit tests.') + else: + print (file_output_string) - unit_test_out_file.close() + unit_test_out_file.close() if args.upload: helpers.directory_copy(results_dir, target_dir) remote_html_file_path = os.path.join(target_dir, html_file_name) - remote_unit_test_out_file = os.path.join(target_dir, unit_test_filename) + remote_unit_test_out_file = os.path.join(target_dir, unit_test_filename); + + link_file_path = target_dir + 'results.link' + + print ('\nOpen file to view results: ' + remote_html_file_path + '\n\n'); if os.name == 'nt': os.system("start " + remote_html_file_path) @@ -453,13 +379,14 @@ def main(): else: webbrowser.open('file://' + os.path.abspath(remote_html_file_path)) webbrowser.open('file://' + os.path.abspath(remote_unit_test_out_file)) + else: # Open it up. if os.name == 'nt': - os.system("start " + html_file_path) + os.system("start " + html_file_full_path) os.system("start " + unit_test_outputfile_path) else: - webbrowser.open('file://' + os.path.abspath(html_file_path)) + webbrowser.open('file://' + os.path.abspath(html_file_full_path)) webbrowser.open('file://' + os.path.abspath(unit_test_outputfile_path)) print('Done') diff --git a/Tests/StartBuildTest.py b/Tests/Old/StartBuildTest.py similarity index 75% rename from Tests/StartBuildTest.py rename to Tests/Old/StartBuildTest.py index 721f6bc75..1de81c290 100644 --- a/Tests/StartBuildTest.py +++ b/Tests/Old/StartBuildTest.py @@ -4,9 +4,11 @@ import getpass import argparse import xml.etree.ElementTree as ET +import TeamCityCommon from TeamCityCommon import connect from TeamCityCommon import server_url from TeamCityCommon import project_url +from TeamCityCommon import connect import base64 import os import GetBuildStatus @@ -14,23 +16,10 @@ default_xml_file = './build.xml' queue_url = 'app/rest/buildQueue?locator=project:Falcor' -def start_build_internal(username, password, xml_data): - buildQueue_url = server_url + queue_url - - auth = username + ":" + password - base64string = base64.encodestring(auth.encode() ) - r = urllib.request.Request(buildQueue_url, xml_data, {'Content-Type' : 'application/xml', 'Authorization' : ('Basic %s' % base64string) }) - - try: - returnData = urllib.request.urlopen(r) - except urllib.error.HTTPError as httpErr: - reason = httpErr.reason - print('Error on urlopen ' + str(httpErr.code) + ' . ' + httpErr) - print('Failed to start_build') - - return +def start_build_internal(xml_data): + TeamCityCommon.post_request(queue_url, xml_data, 'application/xml') -def start_build(username, password, xml_file_path, branch_name, git_path, tests_directory, buildTypeId): +def start_build(username, xml_file_path, branch_name, git_path, tests_directory, buildTypeId): file = open(xml_file_path, 'rt') data = file.read() file.close() @@ -42,8 +31,8 @@ def start_build(username, password, xml_file_path, branch_name, git_path, tests_ # insert branch name into correct location for data in xml.iter(): - if data.get('branch'): - data.set('branch', branch_name) + if data.get('branchName'): + data.set('branchName', branch_name) if buildTypeId: if data.get('id'): data.set('id', buildTypeId) @@ -72,7 +61,7 @@ def start_build(username, password, xml_file_path, branch_name, git_path, tests_ # convert back to string to be sent in post request xml_data = ET.tostring(xml) - start_build_internal(username, password, xml_data) + start_build_internal(xml_data) def main(): # Argument Parser. @@ -92,6 +81,8 @@ def main(): else: username = input('Enter username for teamcity.nvidia.com: ') + connect(username) + start_build(username, xml_file = default_xml_file) if __name__ == '__main__': diff --git a/Tests/Old/TeamCityCommon.py b/Tests/Old/TeamCityCommon.py new file mode 100644 index 000000000..7f1e597ec --- /dev/null +++ b/Tests/Old/TeamCityCommon.py @@ -0,0 +1,70 @@ +import urllib.parse +import urllib.request +import getpass +import argparse +import ssl +import base64 +import xml.etree.ElementTree as ET + +server_url = 'https://teamcity.nvidia.com/' +project_url = 'app/rest/builds/?locator=project:Falcor,count:1000' + +sslContext = ssl.SSLContext() +sslContext.verify_mode = ssl.CERT_REQUIRED +sslContext.check_hostname = True +sslContext.load_default_certs() + +connectionTimeOut = 1000; + +def get_request(subpage): + r = urllib.request.Request(server_url + subpage, headers= authentification_header) + r.timeout = connectionTimeOut + handler = urllib.request.HTTPSHandler(context=sslContext) + + try: + return handler.https_open(r) + except urllib.error.HTTPError as httpErr: + reason = httpErr.reason + print('Error on urlopen ' + str(httpErr.code) + ' . ' + httpErr) + +def post_request(subpage, post_data, content_type_str): + + local_authentification_headers = authentification_header + if content_type_str: + local_authentification_headers['Content-Type'] = content_type_str + + r = urllib.request.Request(server_url + subpage, data=post_data, headers= local_authentification_headers) + r.timeout = connectionTimeOut + handler = urllib.request.HTTPSHandler(context=sslContext) + + try: + return handler.https_open(r) + except urllib.error.HTTPError as httpErr: + reason = httpErr.reason + print('Error on urlopen ' + str(httpErr.code) + ' . ' + httpErr) + +authentification_header = {} + +def connect(username, password = ''): + if not username: + raise ConnectError('No username provided for teamcity connection') + + if not password: + getPassPrompt = 'Enter teamcity password for user ' + username + ':' + password = getpass.getpass(prompt=getPassPrompt ) + + auth = base64.b64encode(':'.join([username, password]).encode()).decode('ascii') + global authentification_header + authentification_header = {'Authorization': ('Basic %s:' % auth)} + + r = urllib.request.Request(server_url, headers= authentification_header) + r.timeout = connectionTimeOut + handler = urllib.request.HTTPSHandler(context=sslContext) + + try: + handler.https_open(r) + except urllib.error.HTTPError as httpErr: + reason = httpErr.reason + print('Error on urlopen ' + str(httpErr.code) + ' . ' + httpErr) + print('Failed to start_build') + diff --git a/Tests/WriteTestResultsToHTML.py b/Tests/Old/WriteTestResultsToHTML.py similarity index 98% rename from Tests/WriteTestResultsToHTML.py rename to Tests/Old/WriteTestResultsToHTML.py index 1fe66e911..2844473c5 100644 --- a/Tests/WriteTestResultsToHTML.py +++ b/Tests/Old/WriteTestResultsToHTML.py @@ -98,7 +98,7 @@ def get_image_comparison_table_code(screen_capture_results, errors): # If zero captures, test probably failed to run. Color the test name red for test_result in screen_capture_results[result_key][test_key]: - test_name = os.path.basename(test_result['Source Filename']) + test_name = test_result['Source Filename'] image_table_data = "" if test_name not in table_code_data.keys(): table_code_data[test_name] = {} diff --git a/Tests/ReadMe.txt b/Tests/ReadMe.txt deleted file mode 100644 index 79e36dc85..000000000 --- a/Tests/ReadMe.txt +++ /dev/null @@ -1,14 +0,0 @@ -Running the CheckInTest(D3D12 or VK) uses a relative path from the batch file to the main Falcor Directory (defaults to ../) and uses the TestConfigs by default. -The reference files that are used by default are defined in MachineConfigs.py - -All Test Results from the CheckInTest are placed in the TestsResults\\local-results\\directory - -RunTestsCollection.py runs a TestCollection file from the configs folder. -Pulls from the Repository Target + Source Branch Target to the local Destination Target. -Uses the Compare Reference Target\\(name of the local machine)\\Compare Branch Target\\(Folder for each Test Set (in the array!))\\ - -RunGenerateReferences.py runs a TestCollection file from the configs folder. -Pulls from the Repository Target + Source Branch Target to the local Destination Target. -Uses the Generate Reference Target\\(name of the local machine)\\Source Branch Target\\(Folder for each Test Set (in the array!))\\ - -TODO UPDATE README FOR NEW TESTING STUFFS \ No newline at end of file diff --git a/Tests/TeamCityCommon.py b/Tests/TeamCityCommon.py deleted file mode 100644 index c299be9e0..000000000 --- a/Tests/TeamCityCommon.py +++ /dev/null @@ -1,25 +0,0 @@ -import urllib.parse -import urllib.request -import getpass -import argparse -import xml.etree.ElementTree as ET - -server_url = 'http://teamcity.nvidia.com:80/' -project_url = server_url + 'app/rest/builds/?locator=project:Falcor,count:1000' - -def connect(username, password = ''): - if not username: - raise ConnectError('No username provided for teamcity connection') - - if not password: - getPassPrompt = 'Enter teamcity password for user ' + username + ':' - password = getpass.getpass(prompt=getPassPrompt ) - - pword_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() - pword_mgr.add_password(None, server_url, username, password) # None param is a realm btw - - handler = urllib.request.HTTPBasicAuthHandler(pword_mgr) - opener = urllib.request.build_opener(handler) - opener.open(server_url) - urllib.request.install_opener(opener) - diff --git a/Tests/TestConfig.py b/Tests/TestConfig.py new file mode 100644 index 000000000..01d52b9db --- /dev/null +++ b/Tests/TestConfig.py @@ -0,0 +1,25 @@ +import os + +scenes = [ "Arcade/Arcade.fscene", "SunTemple/SunTemple.fscene", "Bistro/Bistro_Interior.fscene", "Cerberus/Standard/Cerberus.fscene" ] +exitTime = 40 +frames = [ 16, 32, 64, 128, 256, 512, 1024, 2048] +defaultConfig = 'ReleaseD3D12' +localTestDir = 'TestResults' +tolerance = 0.00001 +tolerance_lower = 0.1 +framerate = 60 + +# Relative to root directory +ignoreDirectories = {} +ignoreDirectories['ReleaseD3D12'] = [] +ignoreDirectories['ReleaseVK'] = [ os.path.join('Framework', 'Internals') ] + +# Supported image extensions +imageExtensions = ['.png', '.jpg', '.tga', '.bmp', '.pfm', '.exr'] + +slnFile = "falcor.sln" +mogwaiExe = 'Mogwai' +ultExe = 'FalcorTest' +if os.name == 'nt': + mogwaiExe += '.exe' + ultExe += '.exe' diff --git a/Tests/TestFalcor.py b/Tests/TestFalcor.py new file mode 100644 index 000000000..3fcf8e834 --- /dev/null +++ b/Tests/TestFalcor.py @@ -0,0 +1,83 @@ +import os +import sys +import shutil +import argparse +import TestConfig as testConfig +import Helpers as helpers +import GraphTests as graphTester +import MachineConfigs as machine_configs +import socket + +def run_ults(config): + try: + ult_exe = helpers.findExecutable(config, testConfig.ultExe) + print("Running unit-tests from " + ult_exe) + ret = helpers.runProcessAsync([ult_exe]) + if len(ret): + print(ret) + else: + print("Unit-tests passed") + except BaseException as e: + print('Error when trying to run unit tests. ' + str(e)) + +def save_results(args): + if not os.path.isdir(testConfig.localTestDir): + print("No results were generated, nothing to upload") + return + + ref = helpers.buildRefSubFolder(args) + ref = os.path.join(ref, args.build_id) + ref = os.path.join(machine_configs.results_cache_directory, ref) + print("Saving test results to " + ref) + helpers.rmdir(ref) + shutil.copytree(testConfig.localTestDir, ref) + + +def prepare_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--build_config', action='store', help='Build configuration for test. ReleaseD3D12 by default', default=testConfig.defaultConfig) + parser.add_argument('--rebuild', action='store_true', help='Force solution rebuild') + parser.add_argument('--dont_build', action='store_true', help='Don\'t build the solution. If the solution wasn\'t built, the test will fail' ) + parser.add_argument('--save_results', action='store_true', help='Save the results to a local directory') + parser.add_argument('--build_id', action='store', help='TeamCity build ID', default="") + parser.add_argument('--branch_name', action='store', help='Name of the current checkout branch', default=helpers.getGitBranchName("..")) + parser.add_argument('--machine_name', action='store', help='Optional sub folder name within references directory', default=socket.gethostname()); + parser.add_argument('--clean_packman', action='store_true', help='Delete the packman repository') + parser.add_argument('--vcs_root', action='store', help='The VCS root folder', default=helpers.getVcsRoot("..")); + return parser.parse_args() + +def main(): + success = True + try: + args = prepare_args() + print("Working on GIT branch '" + args.branch_name + "'") + + if(args.clean_packman): + helpers.deletePackmanRepo() + + if not args.dont_build: + helpers.buildSolution("..", "falcor", args.build_config, args.rebuild) + + run_ults(args.build_config) + remote_ref_dir = os.path.join(machine_configs.remote_reference_directory, helpers.buildRefSubFolder(args)) + local_ref_dir = os.path.join(machine_configs.local_reference_directory, helpers.buildRefSubFolder(args)) + print("Mirroring references") + helpers.mirror_folders(remote_ref_dir, local_ref_dir) + print("Running graph tests and comparing against " + local_ref_dir) + success = graphTester.test_all_graphs("../Source", testConfig.localTestDir, "Testing", args.build_config, local_ref_dir) + if not success: print("Graph tests failed") + + except Exception as e: + print("TestFalcor failed. " + str(e)) + success = False + + finally: + if args.save_results: + save_results(args) + if success: + print("All tests passed") + + if not success: sys.exit(-1) + +if __name__ == '__main__': + main() diff --git a/Tests/build.xml b/Tests/build.xml deleted file mode 100644 index d33088f31..000000000 --- a/Tests/build.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Framework/Documentation/Doxyfile b/Tools/Doxyfile similarity index 100% rename from Framework/Documentation/Doxyfile rename to Tools/Doxyfile diff --git a/Framework/SceneScripts/buildTiledScene.py b/Tools/buildTiledScene.py similarity index 100% rename from Framework/SceneScripts/buildTiledScene.py rename to Tools/buildTiledScene.py diff --git a/dependencies.xml b/dependencies.xml deleted file mode 100644 index df42d34c7..000000000 --- a/dependencies.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/update_dependencies.bat b/update_dependencies.bat deleted file mode 100644 index 9cd399aa8..000000000 --- a/update_dependencies.bat +++ /dev/null @@ -1,4 +0,0 @@ -@set PM_DISABLE_VS_WARNING=true -@if not exist %~dp0\Framework\Externals mkdir %~dp0\Framework\Externals -@call "%~dp0packman\packman.cmd " pull "%~dp0dependencies.xml" --platform win -@if errorlevel 1 exit /b 1